Skip to main content

Sort on multiple keys with Underscore's sortBy()

The Underscore library's sortBy() function allows us to sort a collection of objects by a single key name. The enhancement request to sort on multiple fields hasn't been implemented yet.

What to do in the meantime? You could call sort() with your own comparison method, or switch to another helper library like Lodash. That can be more trouble than it's worth.

A quick and dirty alternative is to call sortBy() multiple times in order of the least important field to the most important. This takes advantage of sortBy()'s stable sort algorithm. "Stable sort" means when two items are equal the sorter will leave them in the same order it found them. If we call sortBy multiple times, each pass will nudge it toward its final order. Bucket sort works on the same principle.

Say we want to sort our list by name, then age. To use sortBy, we'll call it in order of the least important key - age - and then the more important key, name.

Here it is step by step.

Given our list:

var list = [
  { name:'Charlie', age:3},
  { name:'Dog', age:1 },
  { name:'Baker', age:7},
  { name:'Abel', age:9 },
  { name:'Baker', age:5 }
];

Sort by the least important key:

var halfSorted = _.sortBy( list, 'age');

which produces:

[ { name: 'Dog', age: 1 },
  { name: 'Charlie', age: 3 },
  { name: 'Baker', age: 5 },
  { name: 'Baker', age: 7 },
  { name: 'Abel', age: 9 } 
]

Sort again on the more important key:

var fullySorted = _.sortBy( halfSorted, 'name');

[ { name: 'Abel', age: 9 },
  { name: 'Baker', age: 5 },
  { name: 'Baker', age: 7 },
  { name: 'Charlie', age: 3 },
  { name: 'Dog', age: 1 } 
]

... which is now sorted by name, then age.

More tersely,

var fullySorted = _.sortBy(( _.sortBy(list, 'age')), 'name');

or

var fullySorted =  _.chain(list).sortBy('age').sortBy('name').value();

This isn't recommended for sorting large lists, since you're calling sort multiple times. The overhead is minimal with a short list.