In this article, I’ll show you how to sort an array of objects in JavaScript with no fuss or bother. If you have an array of objects that you need to sort into a certain order, the temptation might be to reach for a JavaScript library. Before you do however, remember that you can do some pretty neat sorting with the native Array.sort function.
To follow along with this article, you will need a knowledge of basic JavaScript concepts, such as declaring variables, writing functions, and conditional statements. I’ll also be using ES6 syntax.
By default, the JavaScript Array.sort
function converts each element in the array to be sorted, into a string, and compares them in Unicode code point order.
const foo = [8, 3, 5, 'whistle', 'fish'];
foo.sort(); // returns [3, 5, 8, 'fish', 'whistle']
const bar = [4, 19, 30, function(){}, {key: 'value'}];
bar.sort(); // returns [ 19, 30, 4, { key: 'value' }, [Function] ]
You may be wondering why 30 comes before 4. Not logical, huh? Well, actually it is. This happens because each element in the array is first converted to a string, and "30"
comes before "4"
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 = ['hello world', 31, 5, 9, 12];
baz.sort(); // baz array is modified
console.log(baz); // shows [12, 31, 5, 9, "hello world"]
To avoid this, you can create a new instance of the array to be sorted and modify that instead.
const baz = ['hello world', 31, 5, 9, 12];
const newBaz = [...baz].sort(); // new instance of baz array is created and sorted
console.log(baz); // "hello world", 31, 5, 9, 12]
console.log(newBaz); // [12, 31, 5, 9, "hello world"]
Note the use of the spread
operator to create a new instance of baz
. You can read more about that here.
Using Array.sort
alone would not 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 a
and b
are the two elements being compared by the compare function. If the return value of the compare function is:
a
comes before b
b
comes before a
a
and b
are left unchanged with respect to each otherLet’s look at a simple example with an array of numbers:
const arr = [1, 2, 30, 4];
function compare(a, b){
if (a > b) return 1;
if (b > a) return -1;
return 0;
}
arr.sort(compare);
// => 1, 2, 4, 30
This can be refactored somewhat to obtain the return value by subtracting a
from b
:
function compare(a, b){
return a - b;
}
This is now a good candidate for an arrow function:
arr.sort((a, b) => a - b);
If you’re not familiar with arrow functions, you can read more about them here: ES6 Arrow Functions: Fat and Concise Syntax in JavaScript.
Now let’s look at sorting an array of objects. Let’s take an array of band objects:
const bands = [
{ genre: 'Rap', band: 'Migos', albums: 2},
{ genre: 'Pop', band: 'Coldplay', albums: 4},
{ genre: 'Rock', band: 'Breaking Benjamins', albums: 1}
];
We can use the following compare
function to sort this array of objects according to genre:
function compare(a, b) {
// Use toUpperCase() to ignore character casing
const genreA = a.genre.toUpperCase();
const genreB = b.genre.toUpperCase();
let comparison = 0;
if (genreA > genreB) {
comparison = 1;
} else if (genreA < genreB) {
comparison = -1;
}
return comparison;
}
bands.sort(compare);
/* returns [
{ genre: 'Pop', band: 'Coldplay', albums: 4 },
{ genre: 'Rap', band: 'Migos', albums: 2 },
{ genre: 'Rock', band: 'Breaking Benjamins', albums: 1 }
] */
To reverse the sorting order, you can simply 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 bands = [
{ genre: 'Rap', band: 'Migos', albums: 2},
{ genre: 'Pop', band: 'Coldplay', albums: 4, awards: 13},
{ genre: 'Rock', band: 'Breaking Benjamins', albums: 1}
];
// function for dynamic sorting
function compareValues(key, order='asc') {
return function(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
bands.sort(compareValues('band'));
// array is sorted by band in descending order
bands.sort(compareValues('band', 'desc'));
// array is sorted by albums in ascending order
bands.sort(compareValues('albums'));
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 is not defined on the 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 value of the property. 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 peculiarity your script needs.
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 will 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:
["motorhead", "Motorhead", "Mötorhead"].sort();
// ["Motorhead", "Mötorhead", "motorhead"]
["motorhead", "Motorhead", "Mötorhead"].sort((a, b) => a.localeCompare(b));
// ["motorhead", "Motorhead", "Mötorhead"]
In terms of our compareValues
function, that means we could write:
function compareValues(key, order='asc') {
return function(a, b) {
if(!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) return 0;
let 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. Although many JavaScript libraries offer this kind of dynamic sorting ability (e.g. Underscore.js, Lodash and Sugar), as demonstrated, it’s not all that hard to implement this kind of functionality yourself.
If you have any questions or comments, feel free to start a conversation.
Originally published by Olayinka Omole at sitepoint.com
#javascript