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

Why does JavaScript function flawlessly in FireFox, yet fails completely in other web browsers?

When it comes to browsing, FireFox is my go-to browser, especially for testing out my website Avoru. However, I recently encountered an issue when checking the functionality of my code on other major browsers like Google Chrome, Opera, and Safari. It seems ...

Oops! Looks like node.js is throwing an error with the message "connect ECONNREFUSED"

After creating a node.js application that interacts with a locally installed MySQL database, I encountered an issue where running the app through CMD (npm start) returned the desired results, but testing it through Postman resulted in an error: connect E ...

Storing data in a TypeBuffer and then retrieving it from a file can lead to surprising outcomes

Upon executing the following code: var list = new Uint32Array(16); for (var i=0; i<16; ++i) list[i] = i; fs.writeFileSync("list", new Uint8Array(list).buffer); console.log([].slice.call(new Uint32Array(fs.readFileSync("list")))); We anticipate the out ...

Encountering an issue when trying to run npm run dev-server on Windows 10

Having trouble running the dev-server for superset-frontend. Encountering this error message. Any assistance would be greatly valued.https://i.stack.imgur.com/zsVU4.png ...

Include chosen select option in Jquery form submission

Facing some challenges with a section of my code. Essentially, new elements are dynamically added to the page using .html() and ajax response. You can see an example of the added elements in the code. Since the elements were inserted into the page using . ...

Utilizing MEAN.JS in combination with angular-translate for seamless translation

Currently in the process of validating Mean.JS version 0.4.1. Working on integrating multilingual support into a sample application. Referencing the article located at . Installed the following packages using bower: "angular-translate": "~2.5.2", "angu ...

AngularJS's $http.get function has the ability to read text files, but it struggles with reading JSON

I'm new to angularjs and I am struggling to retrieve data from a json file using the $http.get method. It seems to work fine when I try to read a simple txt file, but not with the json file. I can't seem to pinpoint where the issue lies... Belo ...

Is there a way to verify HTML binding prior to setting up an AngularJS directive?

On a page where I utilized a custom select-box directive to display the Month, certain arguments are required by the directive: <custom-select-box id="month" model="month" model-required model-name="month" options="month.value ...

Controlling the window opener; Inserting text into an element in the parent window

A pop-up window allows users to select files, then displays the selected image's URL. However, I need to take additional steps beyond that. I am seeking a method to input the URL into an input element on the parent window. window.opener.document.wri ...

What are some effective ways to slow down the image transitions in a Javascript slideshow?

I am currently developing a slideshow that updates Images, Title, and Description simultaneously based on their Array index. The slideshow is functional BUT, my goal is to achieve a smooth transition to the next/previous Image (... title & descript ...

Check if two PHP arrays are the mirror image of each other

It seems like a simple task that I might be overlooking, but I need to solve this. This question is related to a class I'm taking, and I don't expect anyone to do the work for me (I won't learn that way). I'm looking for a good starting ...

Disabling the submit button after submitting the form results in the page failing to load

I am encountering an issue with my HTML form that submits to another page via POST. After the form validates, I attempt to disable or hide the submit button to prevent double submission and inform the user that the next page may take some time to load. He ...

How to iterate over the request body in Node.js using Express?

When I send a request with data in the form of an array of objects: [ {id: "1"}, {id: "2"}, {id: "3"} ] I am utilizing JSON.stringify() and my req.body ends up looking like this: { '{"id":"1"} ...

Encountering undefined id in AngularJS service

I recently went through a tutorial at However, I encountered an issue when attempting to save a new record. The value of newcontact.id is showing as undefined and I'm unable to save the new record. Any ideas on what might be causing this problem? ...

The function Sync in the cp method of fs.default is not a valid function

When attempting to install TurboRepo, I encountered an issue after selecting npm. >>> TURBOREPO >>> Welcome to Turborepo! Let's get you set up with a new codebase. ? Where would you like to create your turborepo? ./my-turborepo ...

Ways to remove specific characters from the escape() function in express-validators

When using the check method from express-validator to validate user input, I'm curious if there's a way to exclude certain characters from the test. For example, currently I have: check("profile.about").trim().escape() This code snippet convert ...

Embracing the power of dynamic imports in Next.js 10 with SDK integration for

I attempted to dynamically import the TRTC SDK using Next.js 10: const TRTC = dynamic(() => import('trtc-js-sdk').then((module) => module.NamedExport), { ssr: false }); However, I encountered an error stating "TRTC.createClient is not a co ...

What is the process for populating dropdown options from state?

I've been struggling to populate a select element with options based on an array in state. Despite trying various methods, the code snippet below seems to be the most detailed (I'm still getting familiar with React after taking a break for a few ...

Dynamic Dropdown Menu in Zend Framework with Autofill Feature

I've been diligently working on a code to automatically populate dropdowns onchange, right after selecting the necessary values from an autocomplete search field. However, I am facing an issue where my autofill feature fails to work after making a sel ...

Endless cycle in Vue-Router when redirecting routes

I need advice on how to properly redirect non-authenticated users to the login page when using JWT tokens for authentication. My current approach involves using the router.beforeEach() method in my route configuration, but I'm encountering an issue wi ...