Underscore: sortBy() based on multiple attributes

sortBy says that it is a stable sort algorithm so you should be able to sort by your second property first, then sort again by your first property, like this:

var sortedArray = _(patients).chain().sortBy(function(patient) {
    return patient[0].name;
}).sortBy(function(patient) {
    return patient[0].roomNumber;
}).value();

When the second sortBy finds that John and Lisa have the same room number it will keep them in the order it found them, which the first sortBy set to "Lisa, John".


Here's a hacky trick I sometimes use in these cases: combine the properties in such a way that the result will be sortable:

var sortedArray = _.sortBy(patients, function(patient) {
  return [patient[0].roomNumber, patient[0].name].join("_");
});

However, as I said, that's pretty hacky. To do this properly you'd probably want to actually use the core JavaScript sort method:

patients.sort(function(x, y) {
  var roomX = x[0].roomNumber;
  var roomY = y[0].roomNumber;
  if (roomX !== roomY) {
    return compare(roomX, roomY);
  }
  return compare(x[0].name, y[0].name);
});

// General comparison function for convenience
function compare(x, y) {
  if (x === y) {
    return 0;
  }
  return x > y ? 1 : -1;
}

Of course, this will sort your array in place. If you want a sorted copy (like _.sortBy would give you), clone the array first:

function sortOutOfPlace(sequence, sorter) {
  var copy = _.clone(sequence);
  copy.sort(sorter);
  return copy;
}

Out of boredom, I just wrote a general solution (to sort by any arbitrary number of keys) for this as well: have a look.


I know I'm late to the party, but I wanted to add this for those in need of a clean-er and quick-er solution that those already suggested. You can chain sortBy calls in order of least important property to most important property. In the code below I create a new array of patients sorted by Name within RoomNumber from the original array called patients.

var sortedPatients = _.chain(patients)
  .sortBy('Name')
  .sortBy('RoomNumber')
  .value();