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?
btw your initializer for patients is a bit weird, isn’t it?
why don’t you initialize this variable as this -as a true array of objects-you can do it using _.flatten() and not as an array of arrays of single object, maybe it’s typo issue):
I sorted the list differently and add Kiko into Lisa’s bed; just for fun and see what changes would be done…
inspect sorted and you’ll see this
so my answer is : use an array in your callback function
this is quite similar to Dan Tao‘s answer, I just forget the join (maybe because I removed the array of arrays of unique item :))
Using your data structure, then it would be :
and a testload would be interesting…
Perhaps underscore.js or just Javascript engines are different now than when these answers were written, but I was able to solve this by just returning an array of the sort keys.
In action, please see this fiddle: https://jsfiddle.net/mikeular/xenu3u91/
You could concatenate the properties you want to sort by in the iterator:
or something equivalent.
NOTE: Since you are converting the numeric attribute roomNumber to a string, you would have to do something if you had room numbers > 10. Otherwise 11 will come before 2. You can pad with leading zeroes to solve the problem, i.e. 01 instead of 1.
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: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 firstsortBy
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:
However, as I said, that’s pretty hacky. To do this properly you’d probably want to actually use the core JavaScript
sort
method:Of course, this will sort your array in place. If you want a sorted copy (like
_.sortBy
would give you), clone the array first:Out of boredom, I just wrote a general solution (to sort by any arbitrary number of keys) for this as well: have a look.
Simple Example from http://janetriley.net/2014/12/sort-on-multiple-keys-with-underscores-sortby.html (courtesy of @MikeDevenney)
Code
With Your Data
None of these answers are ideal as a general purpose method for using multiple fields in a sort. All of the approaches above are inefficient as they either require sorting the array multiple times (which, on a large enough list could slow things down a lot) or they generate huge amounts of garbage objects that the VM will need to cleanup (and ultimately slowing the program down).
Here’s a solution that is fast, efficient, easily allows reverse sorting, and can be used with
underscore
orlodash
, or directly withArray.sort
The most important part is the
compositeComparator
method, which takes an array of comparator functions and returns a new composite comparator function.You’ll also need a comparator function for comparing the fields you wish to sort on. The
naturalSort
function will create a comparator given a particular field. Writing a comparator for reverse sorting is trivial too.(All the code so far is reusable and could be kept in utility module, for example)
Next, you need to create the composite comparator. For our example, it would look like this:
This will sort by room number, followed by name. Adding additional sort criteria is trivial and does not affect the performance of the sort.
Returns the following
The reason I prefer this method is that it allows fast sorting on an arbitrary number of fields, does not generate a lot of garbage or perform string concatenation inside the sort and can easily be used so that some columns are reverse sorted while order columns use natural sort.
Just return an array of properties you want to sort with:
ES6 Syntax
ES5 Syntax
This does not have any side effects of converting a number to a string.
I think you’d better use
_.orderBy
instead ofsortBy
: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.
If you happen to be using Angular, you can use its number filter in the html file rather than adding any JS or CSS handlers. For example:
In that example, if val = 1234567, it will be displayed as
Example and further guidance at:
https://docs.angularjs.org/api/ng/filter/number
I don’t think most of the answers really work, and certainly there is none that works and uses purely underscore at the same time.
This answer provides sorting for multiple columns, with the ability to reverse the sort order for some of them.
It also builds on the final code step by step, so you may want to take the last code snippet:
I have used this for two columns only (first sort by
a
, then byb
):Here is the result:
I am sure this can be generalized for more than two…
Another version that might be faster:
And a parametrized version of it:
And a parametrized version for any number of columns (seems to work from a first test):
If you want to be able to reverse the column sorting too: