Alter Ego Alter Ego - 26 days ago 5
Javascript Question

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 this 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, wich add 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);
}
};

Answer

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
                }
            });
        });
    });
};