Sorting an array based on shortest distance in Javascript - A step-by-step guide

I need to organize an array so that each element is in the closest proximity to its previous location.

The array looks like this:

locations=[{"loc1",lat,long},{"loc2",lat,long},{"loc3",lat,long},{"loc4",lat,long},{"loc5",lat,long}]

Here's the function for calculating the distance:

var distance = function(lat1, lon1, lat2, lon2)
{
  var radlat1 = Math.PI * lat1/180;
  var radlat2 = Math.PI * lat2/180;
  var theta = lon1-lon2;
  var radtheta = Math.PI * theta/180;
  var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
  dist = Math.acos(dist);
  dist = dist * 180/Math.PI;
  dist = dist * 60 * 1.1515;
  dist = dist * 1.609344 ;

  return dist;
}

When you input values into this function, it provides the distance between two locations.

The starting point is the first element of the locations array. Now, I am looking for a function that can take an array and return the sorted array based on proximity.

Answer №1

If you want to customize the sorting behavior of an array, you can pass a custom function to the sort method in the Array prototype. Here is an example:

locations = [
  ["loc1", 1, 1],
  ["loc2", 3, 3],
  ["loc3", 2, 2],
  ["loc4", 5, 4],
  ["loc5", 3, 5]
];

var distance = function(lat1, lon1, lat2, lon2) {
  var radlat1 = Math.PI * lat1 / 180;
  var radlat2 = Math.PI * lat2 / 180;
  var theta = lon1 - lon2;
  var radtheta = Math.PI * theta / 180;
  var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
  dist = Math.acos(dist);
  dist = dist * 180 / Math.PI;
  dist = dist * 60 * 1.1515;
  dist = dist * 1.609344;

  return dist;
};

locations.sort(function(a, b) {
  var origLat = 0,
    origLong = 0;

  return distance(origLat, origLong, a[1], a[2]) - distance(origLat, origLong, b[1], b[2]);
});

console.log(locations)

Answer №2

To simulate real locations, a hypothetical scenario can be created with the following script:

(I am not familiar with Google Map API, so there might be a more efficient method for this...)

var locations = [{
name : "loc1",
lat : 1001,
long : 2001
}, {
name : "loc2",
lat : 150,
long : 630
}, {
name : "loc3",
lat : 151,
long : 631
}, {
name : "loc4",
lat : 850,
long : 56
}, {
name : "loc5",
lat : 960,
long : 698
}
];

var distance = function (lat1, lon1, lat2, lon2) {
var radlat1 = Math.PI * lat1 / 180;
var radlat2 = Math.PI * lat2 / 180;
var theta = lon1 - lon2;
var radtheta = Math.PI * theta / 180;
var dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
dist = Math.acos(dist);
dist = dist * 180 / Math.PI;
dist = dist * 60 * 1.1515;
dist = dist * 1.609344;

return dist;
}

var locationWithDistFromPrevious = locations.map(function (l, i) {
if (i === 0) {
l.dist = 0;
} else {
l.dist = distance(l.lat, l.long, locations[i - 1].lat, locations[i - 1].long)
}
return l;
}).sort(function (a, b) {
return a.dist - b.dist
});

var locationWithDistFromFirst = locations.map(function (l, i) {
if (i === 0) {
l.dist = 0;
} else {          
l.dist = distance(l.lat, l.long, locations[0].lat, locations[0].long)
}
return l;
}).sort(function (a, b) {
return a.dist - b.dist
});


document.getElementById("resultFromPrev").textContent = JSON.stringify(locationWithDistFromPrevious, null, 4);
document.getElementById("resultFromFirst").textContent = JSON.stringify(locationWithDistFromFirst, null, 4);
<body>
  Sort by previous item<br/>
  <pre id="resultFromPrev"></pre><br/>
  Sort by first item dist <br/>
  <pre id="resultFromFirst"></pre><br/>
</body>

Answer №3

It seems like you're about to embark on a complex journey with multiple twists and turns. Before diving in, I recommend exploring the possibilities of using the Maps API to optimize your driving route for multiple destinations. You can find some guidance on this topic at Google Map V3, how to get list of best driving route for multiple destinations?. By leveraging the optimizeWaypoints setting, you can obtain a list of locations arranged in the most efficient driving order.

While this approach may not provide a direct distance comparison, it could still meet your requirements effectively.

Answer №4

To optimize the sorting process based on distance, it is recommended to include the distance calculation within the sort function:

Considering that distance calculation can be resource-intensive, storing calculated distances in an object can significantly improve performance by preventing redundant calculations during the sorting process:

var startingLoc = {lat:0.000,lng:0.000};//coordinates of the starting location;
var distanceCache = {}; //an object to cache distance calculations

//sort locations - assuming loc1 is {lat:some number, lng: some number}
locations.sort(function(loc1,loc2) {
    var loc1Key = loc1.lat+'-'+loc1.lng;
    var loc2Key = loc2.lat+'-'+loc2.lng;

    if(!distanceCache.hasOwnProperty(loc1Key)) {
      distanceCache[loc1Key] = distance(startingLoc.lat,startingLoc.lng,loc1.lat,loc1.lng);
    }

    if(!distanceCache.hasOwnProperty(loc2Key)) {
      distanceCache[loc2Key] = distance(startingLoc.lat,startingLoc.lng,loc2.lat,loc2.lng);
    }

    return distanceCache[loc1Key] - distanceCache[loc2Key];

 });

distanceCache = null; //clearing distanceCache after use

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Navigate to a particular section in the webpage

There are a few div elements with the class .posts that each have an attribute data-id which corresponds to the ID in a MySQL database. <div class="posts" data-id="1"></div> <div class="posts" data-id="2"></div> If I want to scrol ...

Deciphering a hierarchical JSON array without identifiers in C#

Here is a nested JSON array structure: [ [ [ [1234.5 ,9876,5], [1234.5 ,9876,5] ], [ [1234.5 ,9876,5], [1234.5 ,9876,5] ] ], [ [ [1234.5 ,9876, ...

The internet explorer browser does not support the keypress event

i have the following code snippet: <input class="any" type="text" id="myId" name="myName" /> this specific input is using a jquery datepicker plugin.. (http://jqueryui.com/datepicker/) Here is my JavaScript for this element: $('#myId'). ...

Is the term 'Literal' in Javascript Arrays simply referring to its dynamic nature, allowing it to be modified at any time?

I'm confused why Mozilla refers to this as an Array 'Literal' when it's declared using the VARIABLE keyword and its content can be modified... var drinks = ["Espresso", "Latte", "Cappuccino"]; Could someone shed some light on this for ...

Show AngularJS ListTreeView condensed

I am currently incorporating an angular tree-view feature into my project. You can find the documentation at the following URL: By default, the nodes are displayed in expanded form. However, I would like them to appear collapsed initially and only expand ...

Can you explain the concept of a selenium server?

Just starting with selenium and protractor here. Can someone explain to me if the selenium server acts like a compiler for javascript? And why is it necessary to run it before running the tests? ...

What is the correct way to dynamically switch between RTL and LTR in React with Material UI?

I recently learned that in order to support right-to-left (RTL) languages with Material UI, you need to follow these steps. I have a select input that allows users to switch between languages, changing the overall direction of the app. The core of my appl ...

Extract the content inside an HTML <a> tag with a specified class and auto-populate it into a different text area

I found an HTML tag that is being generated by a WordPress plugin and it contains a random link. My goal is to automatically retrieve this generated link and place it in a textarea within a contact form. The generated code with the link (cannot be modifie ...

Using AJAX to upload jQuery file - sharing PHP variables

After setting up the blueimp jQuery file upload, I've encountered a problem that's stumping me. My goal is to retrieve a PHP variable that's posted when the file is uploaded and execute some PHP code if it exists. I have made modifications ...

When using `parseInt` on the string "802.11 auth", the result did not match my expectations

I am developing a data parser that transforms the output of a command into a JSON object for sending to a live logging and graphing platform. All data extracted by the parser is returned as strings, but I need numerical values to be preserved as numbers. H ...

React Native: How come my text is breaking like this and failing to adhere to the container's margin settings?

I am facing a challenge in React Native where I need to display multiple elements next to each other, with a flex wrap when there are too many elements or if the text is too long. Each element consists of an icon and text, with the text breaking into the n ...

Transforming PHP Variable Using Ajax

I have a variable called $type and I need it to be either month or year. This change should occur when a div is clicked. I attempted to use an onclick event with an ajax call. The ajax call and the variable are both in the same script (index.php). Within ...

What strategies should be considered for styling AngularJS components?

Currently, I am engaged in a project using AngularJS with the intention of gradually organizing things for Angular 6 or whichever version is available when we begin the upgrade process. A significant part of this endeavor involves converting existing direc ...

Incorporating a loading animation with an AJAX call

I've come across various solutions to this issue, one of the most enlightening being a response on Stack Overflow suggesting to "display it as soon as you initiate the AJAX call" and then "hide the image once the request is complete." But how would I ...

The for each loop fails to return the value

In my Vue component, I have a conditional loop that iterates through train stations with 'New York' as the destination or on the route. <tr v-for="departure in filterTrainStations" :key="departure.name"> <td>{{ departure. ...

Animate the div only when the button is clicked

I need a way to hide the text inside a div when it exceeds a certain height, but then expand it to full height upon clicking a button. However, I want this action to only affect the parent div of that specific button, rather than all the divs on the page. ...

Utilizing regular expressions in Javascript to retrieve multiple instances

This paragraph contains a specific string txt = "Local residents o1__have called g__in o22__with reports..."; that requires extracting numbers between each occurrence of o and __ If we use the following regex: txt.match(/o([0-9]+)__/g); We will obtain ...

ajax is triggering the error handler

I am currently developing a web application and I am trying to send data from a PHP controller to JavaScript. After conducting some research, it seems that the most efficient way to accomplish this is by utilizing Ajax and Json. However, I am encountering ...

Binding data to an MVC grid component

I am working with asp.net mvc3.0 razor framework and using an html Table to populate data from ViewBag based on the selected option in a dropdownlist. public ActionResult Index(GetFileName fn) { .... ViewBag.Grid1Data = dt; return View(); } Initially, th ...

Creating a collaborative storage space within a MERN project folder

Currently, I am developing an application using the MERN stack. The structure of my project repository includes both backend and frontend components: my-project/ ├── backend/ │ │ │ . │ . │ └── package.json ├── fronten ...