If you have an array of objects that you need to sort into a certain order, you might be tempted to reach for a JavaScript library. But before you do, remember that you can do some pretty neat sorting with the native Array.sort function.
In this article, we’ll show you how to sort an array of objects in JavaScript with no fuss or bother.
By default, the JavaScript Array.sort
function converts each element in the array that needs to be sorted into a string, and compares them in Unicode code point order.
const foo = [9, 1, 4, 'zebroid', 'afterdeck'];
foo.sort(); // returns [ 1, 4, 9, 'afterdeck', 'zebroid' ]
const bar = [5, 18, 32, new Set, { user: 'Eleanor Roosevelt' }];
bar.sort(); // returns [ 18, 32, 5, { user: 'Eleanor Roosevelt' }, Set {} ]
You may be wondering why 32 comes before 5. Not logical, huh? Well, actually it is. This happens because each element in the array is first converted to a string, and "32"
comes before "5"
in Unicode order.
It’s also worth noting that unlike many other JavaScript array functions, Array.sort
actually changes, or mutates the array it sorts.
const baz = ['My cat ate my homework', 37, 9, 5, 17];
baz.sort(); // baz array is modified
console.log(baz); // shows [ 17, 37, 5, 9, 'My cat ate my homework' ]
To avoid this, you can create a new instance of the array to be sorted and modify that instead. This is possible using an array method that returns a copy of the array. For example, Array.slice:
const sortedBaz = baz.slice().sort(); // a new instance of the baz array is created and sorted
Or if you prefer a newer syntax, you can use the spread operator for the same effect:
const sortedBaz = [...baz].sort(); // a new instance of the baz array is created and sorted
The output is the same in both cases:
console.log(baz); // ['My cat ate my homework', 37, 9, 5, 17];
console.log(sortedBaz); // [ 17, 37, 5, 9, 'My cat ate my homework' ]
Using Array.sort
alone wouldn’t be very useful for sorting an array of objects. Thankfully, the function takes an optional compareFunction
parameter, which causes the array elements to be sorted according to the return value of the compare function.
Let’s say that foo
and bar
are the two elements being compared by the compare function, and the return value of the compare function is set up as follows:
foo
comes before bar
bar
comes before foo
foo
and bar
are left unchanged with respect to each other.Let’s look at a simple example with an array of numbers:
const nums = [79, 48, 12, 4];
function compare(a, b) {
if (a > b) return 1;
if (b > a) return -1;
return 0;
}
nums.sort(compare);
// => 4, 12, 48, 79
We can refactor this a little, as subtracting a
from b
will also give us the return value:
function compare(a, b) {
return a - b;
}
This is now a good candidate for an arrow function:
nums.sort((a, b) => a - b);
Now let’s look at sorting an array of objects. For this demo we’ll use an array of singers:
const singers = [
{ name: 'Steven Tyler', band: 'Aerosmith', born: 1948 },
{ name: 'Karen Carpenter', band: 'The Carpenters', born: 1950 },
{ name: 'Kurt Cobain', band: 'Nirvana', born: 1967 },
{ name: 'Stevie Nicks', band: 'Fleetwood Mac', born: 1948 },
];
We can use the following compare
function to sort this array of singers according to their band:
function compare(a, b) {
// Use toUpperCase() to ignore character casing
const bandA = a.band.toUpperCase();
const bandB = b.band.toUpperCase();
let comparison = 0;
if (bandA > bandB) {
comparison = 1;
} else if (bandA < bandB) {
comparison = -1;
}
return comparison;
}
singers.sort(compare);
/* returns [
{ name: 'Steven Tyler', band: 'Aerosmith', born: 1948 },
{ name: 'Stevie Nicks', band: 'Fleetwood Mac', born: 1948 },
{ name: 'Kurt Cobain', band: 'Nirvana', born: 1967 },
{ name: 'Karen Carpenter', band: 'The Carpenters', born: 1950 }
] */
To reverse the sorting order, you can invert the return value of the compare
function:
function compare(a, b) {
...
//invert return value by multiplying by -1
return comparison * -1;
}
Let’s finish up by making this more dynamic. Let’s create a sorting function, which you can use to sort an array of objects, whose values are either strings or numbers. This function has two parameters — the key we want to sort by and the order of the results (i.e. ascending or descending):
const singers = [
{ name: 'Steven Tyler', band: 'Aerosmith', born: 1948 },
{ name: 'Karen Carpenter', band: 'The Carpenters', born: 1950 },
{ name: 'Kurt Cobain', band: 'Nirvana', born: 1967 },
{ name: 'Stevie Nicks', band: 'Fleetwood Mac', born: 1948 },
];
function compareValues(key, order = 'asc') {
return function innerSort(a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) {
// property doesn't exist on either object
return 0;
}
const varA = (typeof a[key] === 'string')
? a[key].toUpperCase() : a[key];
const varB = (typeof b[key] === 'string')
? b[key].toUpperCase() : b[key];
let comparison = 0;
if (varA > varB) {
comparison = 1;
} else if (varA < varB) {
comparison = -1;
}
return (
(order === 'desc') ? (comparison * -1) : comparison
);
};
}
And this is how you’d use it:
// array is sorted by band, in ascending order by default
singers.sort(compareValues('band'));
// array is sorted by band in descending order
singers.sort(compareValues('band', 'desc'));
// array is sorted by name in ascending order
singers.sort(compareValues('name'));
// array is sorted by date if birth in descending order
singers.sort(compareValues('born', 'desc'));
In the code above, the hasOwnProperty method is used to check if the specified property is defined on each object and has not been inherited via the prototype chain. If it’s not defined on both objects, the function returns 0
, which causes the sort order to remain as is (i.e. the objects remain unchanged with respect to each other).
The typeof operator is also used to check the data type of the property’s value. This allows the function to determine the proper way to sort the array. For example, if the value of the specified property is a string
, a toUpperCase
method is used to convert all its characters to uppercase, so character casing is ignored when sorting.
You can adjust the above function to accommodate other data types, and any other needs your script may have.
In our example above, we want to be able to sort an array of objects, whose values are either strings or numbers. If, however, you know that you’ll only be dealing with objects whose values are strings, you can tidy up the code a little using JavaScript’s localeCompare
method.
This method returns a number indicating whether a string comes before, after, or is the same as a given string in the sort order. It enables a case-insensitive sort of an array:
['bjork', 'Bjork', 'Björk'].sort();
// [ 'Bjork', 'Björk', 'bjork' ]
['bjork', 'Bjork', 'Björk'].sort((a, b) => a.localeCompare(b));
// [ 'bjork', 'Bjork', 'Björk' ]
In terms of our compareValues
function, that means we could write:
function compareValues(key, order = 'asc') {
return function innerSort(a, b) {
if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0;
const comparison = a[key].localeCompare(b[key]);
return (
(order === 'desc') ? (comparison * -1) : comparison
);
};
}
You can read more about localeCompare over on MDN.
So there you have it — a short introduction to sorting an array of objects using vanilla JavaScript. Although many libraries offer this kind of dynamic sorting ability, as demonstrated, it’s not all that hard to implement this functionality yourself. Plus it’s good to understand what is going on under the hood.
#JavaScript #Array #Webdev #ES6