Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use lodash/underscore to sort by multiple nested fields?

I want to do something like this:

var data = [
    {
        sortData: {a: 'a', b: 2}
    },
    {
        sortData: {a: 'a', b: 1}
    },
    {
        sortData: {a: 'b', b: 5}
    },
    {
        sortData: {a: 'a', b: 3}
    }
];

data = _.sortBy(data, ["sortData.a", "sortData.b"]);

_.map(data, function(element) {console.log(element.sortData.a + " " + element.sortData.b);});

And have it output this:

"a 1"
"a 2"
"a 3"
"b 5"

Unfortunately, this doesn't work and the array remains sorted in its original form. This would work if the fields weren't nested inside the sortData. How can I use lodash/underscore to sort an array of objects by more than one nested field?

I've turned this into a lodash feature request: https://github.com/lodash/lodash/issues/581

like image 972
Daniel Kaplan Avatar asked Jun 08 '14 22:06

Daniel Kaplan


4 Answers

Update: See the comments below, this is not a good solution in most cases.


Someone kindly answered in the issue I created. Here's his answer, inlined:

_.sortBy(data, function(item) {
   return [item.sortData.a, item.sortData.b];
});

I didn't realize that you're allowed to return an array from that function. The documentation doesn't mention that.

like image 86
Daniel Kaplan Avatar answered Nov 04 '22 21:11

Daniel Kaplan


If you need to specify the sort direction, you can use _.orderBy with the array of functions syntax from Lodash 4.x:

_.orderBy(data, [
  function (item) { return item.sortData.a; },
  function (item) { return item.sortData.b; }
], ["asc", "desc"]);

This will sort first ascending by property a, and for objects that have the same value for property a, will sort them descending by property b.

It works as expected when the a and b properties have different types.

Here is a jsbin example using this syntax.

like image 35
kenjiru Avatar answered Nov 04 '22 20:11

kenjiru


There is a _.sortByAll method in lodash version 3:

https://github.com/lodash/lodash/blob/3.10.1/doc/README.md#_sortbyallcollection-iteratees

Lodash version 4, it has been unified:

https://lodash.com/docs#sortBy

Other option would be to sort values yourself:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort

function compareValues(v1, v2) {
    return (v1 > v2) 
        ? 1 
        : (v1 < v2 ? -1 : 0);
};


var data = [
    { a: 2, b: 1 },
    { a: 2, b: 2 },
    { a: 1, b: 3 }
];

data.sort(function (x, y) {
    var result = compareValues(x.a, y.a);

    return result === 0 
        ? compareValues(x.b, y.b) 
        : result;
});

// data after sort:
// [
//     { a: 1, b: 3 },
//     { a: 2, b: 1 },
//     { a: 2, b: 2 }
// ];
like image 22
Tomas Kirda Avatar answered Nov 04 '22 20:11

Tomas Kirda


The awesome, simple way is:

_.sortBy(data, [function(item) {
    return item.sortData.a;
}, function(item) {
    return item.sortData.b;
}]);

I found it from check the source code of lodash, it always check the function one by one.

Hope that help.

like image 14
KimKha Avatar answered Nov 04 '22 21:11

KimKha