Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lodash sortBy then groupBy, is order maintained?

I'm having trouble figuring out from the lodash documentation if my assumption about sorting and grouping is correct.

If I use sortBy, then use groupBy, do the arrays produced by groupBy maintain the sort order of items?

For example, say I have the following array:

var testArray = [[5,6],[1,3],[5,4],[5,1]]

And I would like to group these by their first element, but also have them sorted by their second element within these groups. So, in lodash I assume I can do the following:

_.chain(testArray)
  .sortBy(function (item) { return item[1]; })
  .groupBy(function (item) { return item[0]; })
  .value()

Which ends up producing what I would expect it to:

{
  1: [[1,3]]
  5: [[5,1],[5,4],[5,6]]
}

Is this just coincidence? Is there anything about how sortBy and groupBy work that ensures this ordering of the grouped arrays? The documentation says that sortBy is a stable sort, does that in the same way apply to groupBy? Is there any reason I should not assume this will work every time?

like image 635
kand Avatar asked Mar 04 '15 17:03

kand


People also ask

Does Lodash groupBy preserve order?

groupBy , but it does preserve the order of array-like collections, and that's probably unlikely to change. So the sub-items within groups would retain their original ordering, but the grouped key ordering may change, because they are object properties.

How do you use sortBy Lodash?

Lodash helps in working with arrays, collection, strings, objects, numbers etc. The _. sortBy() method creates an array of elements which is sorted in ascending order by the results of running each element in a collection through each iteratee.


2 Answers

It's not. Here's example, where order is not retained:

const data = [
  {
    item: 'item1',
    group: 'g2'
  },   {
    item: 'item2',
    group: 'g3'
  },   {
    item: 'item3',
    group: 'g1'
  },   {
    item: 'item4',
    group: 'g2'
  },   {
    item: 'item5',
    group: 'g3'
  }
]

const groupedItems = _(data).groupBy(item => item.group).value()

In this case one would expect that group order would be: g2, g3, g1 - reality is that they are sorted g1, g2, g3.

You can re-sort them with original array though.

const groupedItems = _(data)
  .groupBy(item => item.group)
  .sortBy(group => data.indexOf(group[0]))
  .value()

This will ensure original order of items.

like image 156
Hatch Avatar answered Sep 20 '22 16:09

Hatch


The current implementation of _.groupBy is:

// An internal function used for aggregate "group by" operations.
var group = function(behavior) {
  return function(obj, iteratee, context) {
    var result = {};
    iteratee = cb(iteratee, context);
    _.each(obj, function(value, index) {
      var key = iteratee(value, index, obj);
      behavior(result, value, key);
    });
    return result;
  };
};

// Groups the object's values by a criterion. Pass either a string attribute
// to group by, or a function that returns the criterion.
_.groupBy = group(function(result, value, key) {
  if (_.has(result, key)) result[key].push(value); else result[key] = [value];
});

Basically it iterates through each of the items in the collection in order (if the collection is array-like, which it would be after a sortBy), and pushes them to an array based on their key value.

So yes, I'm not sure if this is an "official" characteristic of _.groupBy, but it does preserve the order of array-like collections, and that's probably unlikely to change.

like image 26
JLRishe Avatar answered Sep 18 '22 16:09

JLRishe