Christian R Christian R - 2 months ago 25
Javascript Question

Underscore: sortBy() based on multiple attributes

I am trying to sort an array with objects based on multiple attributes. I.e if the first attribute is the same between two objects a second attribute should be used to comapare the two objects. For example, consider the following array:

var patients = [
[{name: 'John', roomNumber: 1, bedNumber: 1}],
[{name: 'Lisa', roomNumber: 1, bedNumber: 2}],
[{name: 'Chris', roomNumber: 2, bedNumber: 1}],
[{name: 'Omar', roomNumber: 3, bedNumber: 1}]
];


Sorting these by the
roomNumber
attribute i would use the following code:

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


This works fine, but how do i proceed so that 'John' and 'Lisa' will be sorted properly?

Answer

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.