How to Dynamically Sort an Array of Objects in JavaScript

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.

Sort an array of objects in JavaScript

Basic Array Sorting

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' ]

Try it out

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.

Using Compare Functions to Sort

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:

  1. less than 0 — foo comes before bar
  2. greater than 0  — bar comes before foo
  3. equal to 0  — 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);

How to Sort an Array of Objects in JavaScript

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

Try it out

Creating a Dynamic Sorting Function

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

Try it out

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.

String.prototype.localeCompare()

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.

Conclusion

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

How to Dynamically Sort an Array of Objects in JavaScript
137.30 GEEK