0

There are many questions regarding sorting with JavaScript but I didn't find anything that addresses this case so I don't believe this is a duplicate.

I'm getting data like this back from an api:

//items array 
var items = [{id:1, name:'bill'}, {id:2, name:'sam'}, {id:3, name: mary}, {id:4, name:'jane'}]

//sort order array
var order = [{id:1, sortindex:4}, {id:2, sortindex:2}, {id:3, sortindex: 1}, {id:4, sortindex:3}]

How can I sort the items array by the sortindex property of the objects in the order array? The objects in the two arrays have the common property id. Is there an elegant lodash/underscore solution?

3
  • Is this an API you control? Why wouldn't you just return sorted data? This seems bananas to me. Commented Mar 5, 2015 at 0:21
  • 1
    @m59 your solution using lodash best answered my question. Commented Mar 20, 2015 at 0:25
  • @Mathletics Although I don't have complete control over what is returned from the API, your point to consider looking there is well taken. This is also just a small piece of a more complex problem. Commented Mar 20, 2015 at 0:27

5 Answers 5

1

It's straightforward with lodash. The sortBy function will move the items to the sortindex position, so we just need to use find to get the corresponding object, and return its sortindex property for sortBy to use.

var items = [{id:1, name:'bill'}, {id:2, name:'sam'}, {id:3, name: 'mary'}, {id:4, name:'jane'}];
var order = [{id:1, sortindex:4}, {id:2, sortindex:2}, {id:3, sortindex: 1}, {id:4, sortindex:3}];

var sorted = _.sortBy(items, function(item) {
  // sorts by the value returned here
  return _.find(order, function(ordItem) {
    // find the object where the id's match
    return item.id === ordItem.id;
  }).sortindex; // that object's sortindex property is returned
});

document.body.textContent = JSON.stringify(sorted);
<script src="https://rawgit.com/lodash/lodash/3.0.1/lodash.min.js"></script>

Sign up to request clarification or add additional context in comments.

Comments

0

If you store your order as a map, it's rather straight forward with vanilla Javascript.

var items = [{id:1, name:'bill'}, {id:2, name:'sam'}, {id:3, name: 'mary'}, {id:4, name:'jane'}];
var order = {1:4, 2:2, 3:1, 4:3};
items.sort(function (a, b) {
    return (order[a.id] > order[b.id]) - (order[a.id] < order[b.id]);
});

Or if you insist on your original data, assuming it's already sorted as you show it to be:

var items = [{id:1, name:'bill'}, {id:2, name:'sam'}, {id:3, name: 'mary'}, {id:4, name:'jane'}];
var order = [{id:1, sortindex:4}, {id:2, sortindex:2}, {id:3, sortindex: 1}, {id:4, sortindex:3}];

items.sort(function (a, b) {
    var ai = order[a.id - 1].sortindex;
    var bi = order[b.id - 1].sortindex;
    return (ai > bi) - (ai < bi);
});

If you can't assume the data is sorted, see this question, for example on how to create a map from your data.

3 Comments

What if OP cannot change the data structure?
It's trivial to run over the order array and turn it into a map.
I agree with you, but do you think it's trivial for OP?
0

See in demo: http://jsbin.com/qaquzi/1/edit?js,console

//items array 
var items = [{id:1, name:'bill'}, {id:2, name:'sam'}, {id:3, name: 'mary'}, {id:4, name:'jane'}]

//sort order array
var order = [{id:1, sortindex:4}, {id:2, sortindex:2}, {id:3, sortindex: 1}, {id:4, sortindex:3}]


var sortedOrder = _.sortBy(order, 'sortindex');

var bb = _.map(sortedOrder, function (i) {
    return i.id;
})

var sorted = [];

for (var i = 0, ii = bb.length; i < ii; i++) {
    for (var m = 0, mm = items.length; m < mm; m++) {
        var a = items[m];
        if (a.id == bb[i]) {
            sorted.push(items[m]);
        }
    }
}

console.log(sorted);

Comments

0

Here's my solution:

var items = [
    { id: 1, name: 'bill' },
    { id: 2, name: 'sam' },
    { id: 3, name: 'mary' },
    { id: 4, name: 'jane' }
];

var order = [
    { id: 1, sortindex: 4 },
    { id: 2, sortindex: 2 },
    { id: 3, sortindex: 1 },
    { id: 4, sortindex: 3 }
];

_(items)
    .indexBy('id')
    .at(_.pluck(_.sortBy(order, 'sortindex'), 'id'))
    .pluck('name')
    .value();
// → [ 'mary', 'sam', 'jane', 'bill' ]

Here's what's going on:

  • The indexBy() function transforms items into an object, where the id values are the keys.
  • The at() function gets values from the object, in order, of the passed in keys.
  • The sortBy() function sorts order by the sortindex key.
  • The pluck() function gets the sorted id array.

Comments

0

Here's my solution. If you know that both arrays will match in length and location of id's, then this is a concise solution:

_.chain(items)
 .merge(order)
 .sortBy('sortindex')
 .map(_.partialRight(_.omit, 'sortindex'))
 .value()

Otherwise, if they aren't guaranteed to be sorted, then the items can be resolved with a map/find/merge.

_.chain(items)
 .map(function(item) { 
    return _.merge(item, _.find(order, {'id': item.id})); 
 })
 .sortBy('sortindex')
 .map(_.partialRight(_.omit, 'sortindex'))
 .value()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.