javascriptjqueryarraysjsonsorting

jQuery sort function not working properly


Based on the user location and a store location I try to figure out what the distance is between the two. This is working, but what I want is an array with all the values and sort on the distance between the two points.

I have my add_stores_to_array function, which adds all stores to the array stores when it is looping through the JSON file.

add_stores_to_array = function(position) {
    var user_latitude = position.coords.latitude;
    var user_longitude = position.coords.longitude;

    $.getJSON('/stores').done(function(data) {

        $.each(data.features, function(i, item) {
            var store_latitude = item.geometry.coordinates[1];
            var store_longitude = item.geometry.coordinates[0];

            var user = new google.maps.LatLng(user_latitude, user_longitude);
            var store = new google.maps.LatLng(store_latitude, store_longitude);
            
            var directionsService = new google.maps.DirectionsService();
            
            var request = {
                origin:user,
                destination:store,
                travelMode: google.maps.DirectionsTravelMode.DRIVING
            };

            directionsService.route(request, function(response, status) {
                if (status == google.maps.DirectionsStatus.OK) {
                    var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);

                    // add distance and store id to the array stores
                    stores.push({distance: response, id: item.properties.Nid});
                }
            });
        });

        // call the sort function
        sort_stores(stores);

        console.log(stores);

    });
};

After the $.each I call the sort function. But after logging it to the console, it is still not sorted.

My sort_stores function:

sort_stores = function(stores){
    stores.sort(function(a, b){
        return a.distance - b.distance;
    });
};

First I thought it wasn't working because the $.each was still running, but after adding this code, it still doesn't working:

if (i == Object.keys(data.features).pop()) {
    sort_stores(stores);
}   

So, I tried something different. I call the sort_stores(stores) function in the $.each.

directionsService.route(request, function(response, status) {
    if (status == google.maps.DirectionsStatus.OK) {
        var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);

        stores.push({distance: response, id: item.properties.Nid});
        sort_stores(stores);
    }
});

And it works... the array is sorted based on the value distance in the array. But now he sorts the array after each added store... not really effective.

Is there a proper way to call the sort_stores(stores) function one time, and sort it when all stores are added to the array?

EDIT:

If I place an alert() before the sort_stores(stores) it is working..

                if (status == google.maps.DirectionsStatus.OK) {
                    var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);

                    stores.push({distance: response, id: item.properties.Nid});
                }
            });
        });
        
        alert('Call the sort_stores(stores) function after the $.each, with an alert.. it is working?');
        sort_stores(stores);
    });
};

Edit 2:

Normally I call the function add_stores_to_array from here?

get_user_location = function(){
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(add_stores_to_array);
    }
};

Solution

  • There's nothing wrong with your sort function. The problem is that directionsService.route is asynchronous call and the rest of the code will run even when all the calls are not completed yet.

    You can use jQuery.when(). Here is the new add_stores_to_array() function

    add_stores_to_array = function(position) {
        var promises = []; //ADDED promise array
        var user_latitude = position.coords.latitude;
        var user_longitude = position.coords.longitude;
    
        $.getJSON('/stores').done(function(data) {
            $.each(data.features, function(i, item) {
                var store_latitude = item.geometry.coordinates[1];
                var store_longitude = item.geometry.coordinates[0];
    
                var user = new google.maps.LatLng(user_latitude, user_longitude);
                var store = new google.maps.LatLng(store_latitude, store_longitude);
    
                var directionsService = new google.maps.DirectionsService();
    
                var request = {
                    origin:user,
                    destination:store,
                    travelMode: google.maps.DirectionsTravelMode.DRIVING
                };
    
                var dfd = directionsService.route(request, function(response, status) {
                    if (status == google.maps.DirectionsStatus.OK) {
                        var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
    
                        // add distance and store id to the array stores
                        stores.push({distance: response, id: item.properties.Nid});
                    }
                });
    
                promises.push(dfd); //ADDED store each object in array
            });
    
            //Now you can do the following without having any async issue.
            $.when.apply(null, promises).done(function() { 
               /* sort & do stuff here */ 
               sort_stores(stores);
               console.log(stores);
            });
        });
    };
    

    EDIT

    Here is another approach. Since you need to wait until all responses are returned, you can customize your sort function to check for the response count. If it's equal to total (which means all calls have finished successfully) then sort the array.

    sort_stores = function(stores, responseCount, totalCount ) {
        if (responseCount == totalCount) {
            stores.sort(function(a, b){
                return a.distance - b.distance;
            });
        }
    };
    

    Then change the add_stores_to_array function as follows.

    add_stores_to_array = function(position) {
        var user_latitude = position.coords.latitude;
        var user_longitude = position.coords.longitude;
    
        $.getJSON('/stores').done(function(data) {
            var totalCount = data.features.length; //ADDED Get total count
            var responseCount = 0; //ADDED
            $.each(data.features, function(i, item) {
                var store_latitude = item.geometry.coordinates[1];
                var store_longitude = item.geometry.coordinates[0];
    
                var user = new google.maps.LatLng(user_latitude, user_longitude);
                var store = new google.maps.LatLng(store_latitude, store_longitude);
    
                var directionsService = new google.maps.DirectionsService();
    
                var request = {
                    origin:user,
                    destination:store,
                    travelMode: google.maps.DirectionsTravelMode.DRIVING
                };
    
                directionsService.route(request, function(response, status) {
                    if (status == google.maps.DirectionsStatus.OK) {
                        var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
    
                        // add distance and store id to the array stores
                        stores.push({distance: response, id: item.properties.Nid});
                        responseCount++; //ADDED
                        sort_stores(stores, responseCount, totalCount); //ADDED Call sort function here
                    }
                });
            });
        });
    };