To sort an array in JavaScript, we use the
array.sort() method.
Let's take a look at the following code:
var words = ["andreas", "john", "grech", "janica", "buhagiar"];
var numbers = [5, 2, 8, 666, 42, 13];
console.log(words.sort());
console.log(numbers.sort());
The results of the above are:
["andreas", "buhagiar", "grech", "janica", "john"]
[13, 2, 42, 5, 666, 8]
As you can see, the strings are sorted correctly, but what happened to the numbers? 13 is obviously greater than 2, so why is it before?
Sorting lexicographically
The problem with the
array.sort function is that it sorts lexicographically, meaning it sorts 'alphabetically'; in dictionary order; and thus the
sort function just assumes that the elements that need to be sorted are strings. And if they aren't strings, it converts them to strings and then compares them.
Fortunately,
sort takes also a single parameter; a function that will be used to sort the array with.
Let's take an example of how we can sort numbers:
var numbers = [5, 2, 8, 666, 42, 13];
var ascNumSort = function (a, b) {
return a - b;
};
var ns = numbers.sort(ascNumSort);
console.log(ns);
Now we created a function called
ascNumSort and passed it to the
sort method, and as you can see the numbers are now correctly sorted in ascending order.
What kind of function ?
The function that you pass as a parameter to the
sort method needs to abide with the following rules:
- It must accept two formal arguments
- Return a 0 if the two elements are equal
- Return a positive number if the second parameter should come before the first parameter
- Return a negative number if the first parameter should come before the second parameter
With these rules, we can now build our custom sorting functions.
Let's say that we have an array of objects representing people and we want to sort these people according to their height, in descending order:
var people = [
{name: "Andreas", height: 5.4},
{name: "Janica", height: 4.3},
{name: "Cameron", height: 5.8},
{name: "Richard", height: 5.4},
{name: "Ryan", height: 4.5}
];
var sortedPeople = people.sort(function (p1, p2) {
return p2.height - p1.height;
});
console.log(sortedPeople);
/*Result:
[
Object name=Cameron height=5.8,
Object name=Andreas height=5.4,
Object name=Richard height=5.4,
Object name=Ryan height=4.5,
Object name=Janica height=4.3
]
*/
Sorting on multiple keys
By implementing what we did above, but in a more general way, we can come up with a function that will allow us to sort an array of objects, given a key (or multiple keys) [
taken from Douglas Crockford's The Good Parts]:
//Function by takes a member name string and an
//optional minor comparison function and returns
//a comparison function that can be used to sort an
//array of objects that contain that member. The
//minor comparison function is used to break ties
//when the o[name] and p[name] are equal
var by = function (name, minor) {
return function (o, p) {
var a, b;
if (typeof o === 'object' && typeof p === 'object' && o && p) {
a = o[name];
b = p[name];
if (a === b) {
return typeof minor === 'function' ? minor(o, p) : o;
}
if (typeof a === typeof b) {
return a < b ? -1 : 1;
}
return typeof a < typeof b ? -1 : 1;
} else {
throw {
name: 'Error',
message: 'Expected an object when sorting by ' + name
};
}
}
};
Now let's take a look of an example of how we can use the above
by function:
var people = [
{name: "Andreas", height: 5.4},
{name: "Janica", height: 4.3},
{name: "Tony", height: 5.8},
{name: "Richard", height: 5.4},
{name: "Ryan", height: 4.5},
{name: "Cameron", height: 5.8}
];
console.log(people.sort(by('height')));
/*Result:
[
Object name=Janica height=4.3, Object name=Ryan height=4.5,
Object name=Andreas height=5.4, Object name=Richard height=5.4,
Object name=Tony height=5.8, Object name=Cameron height=5.8
]
*/
console.log(people.sort(by('height', by('name'))));
/*Result:
[
Object name=Janica height=4.3, Object name=Ryan height=4.5,
Object name=Andreas height=5.4, Object name=Richard height=5.4,
Object name=Cameron height=5.8, Object name=Tony height=5.8
]
*/