11

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?

kand
  • 2,080
  • 6
  • 29
  • 41

3 Answers3

16

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.

Hatch
  • 558
  • 5
  • 8
  • 3
    This is pure gold: `.sortBy(group => data.indexOf(group[0]))` . Saved me an hour in trying to get the damn thing working! – Boris Marinov Feb 14 '19 at 08:34
  • 1
    Maybe there was a change to the library, but your first examples output comes out in the group order expected. I'm using lodash v4.17.15 – philip yoo Aug 19 '19 at 19:12
  • I don't think that's what the question is about. Of course the groups are not sorted, because object properties are not sorted (the `groupBy` gives an object, not an array!). The question is about the order inside each group, not about the order of the groups. – Ella Sharakanski May 27 '20 at 10:40
11

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.

JLRishe
  • 90,548
  • 14
  • 117
  • 150
  • 5
    So the sub-items within groups would retain their original ordering, but the grouped key ordering may change, because they are object properties. – Sam Barnum Apr 05 '16 at 00:12
1

Function groupBy returns object. Object doesn't save property order. Does JavaScript Guarantee Object Property Order?

But group arrays saves order, because thay are added with push function.

Community
  • 1
  • 1
Denis535
  • 2,770
  • 1
  • 21
  • 30
  • My question was about the ordering of the resulting arrays, not the parent object produced by `groupBy`. – kand Mar 09 '16 at 21:31
  • Update a little my answer. Just I had trouble with object property order and I decided to write this. – Denis535 Mar 10 '16 at 09:39