BlackHoleGalaxy
BlackHoleGalaxy

Reputation: 9672

groupBy and sortBy an array of object in native ES6

We have an array of objects and we need to group these objects by group ID.

Then we need to obtain an array of array with sorted groups by timestamp.

This is the initial collection:

const arr = [
  {id: 'id1', group: '123', timestamp: 222},
  {id: 'id2', group: '123', timestamp: 999 },
  {id: 'id3', group: '456', timestamp: 333 },
  {id: 'id4', group: '789', timestamp: 111 },
  {id: 'id5', group: '554', timestamp: 555 },
];

The result we try to obtain is:

result = [
  [
    {id: 'id4', group: '789', timestamp: 111 }, 
  ],
  [
    {id: 'id1', group: '123', timestamp: 222},
    {id: 'id2', group: '123', timestamp: 999 },
  ],
  [
    {id: 'id3', group: '456', timestamp: 333 },
  ],
  [
    {id: 'id5', group: '554', timestamp: 555 },
  ]
]

We achieved that using Underscore.js but we are looking for a solution using native javascript and we struggled to pass from a reduced object to an array of array.

var groups = _(arr).chain()
    .groupBy("group")
    .sortBy((data) => _.first(data).timestamp)
    .value();

https://jsfiddle.net/tdsow1ph/

We tried to first group the items using such a method:

const groupBy = (arr, propertyToGroupBy) => {
    return arr.reduce((groups, item) => {
      const val = item[propertyToGroupBy];

      groups[val] = groups[val] || [];
      groups[val].push(item);

      return groups;
    }, {});
  }

But we obtain an unsortable object. So we need to convert that into array of array but we struggle to do that then apply sorting on the timestamp value.

Upvotes: 1

Views: 778

Answers (2)

Eddie
Eddie

Reputation: 26844

You can use reduce() to group the array into an object using the group as the key. Use Object.values() to convert the object into an array.

const arr = [{"id":"id1","group":"123","timestamp":222},{"id":"id2","group":"123","timestamp":999},{"id":"id3","group":"456","timestamp":333},{"id":"id4","group":"789","timestamp":111},{"id":"id5","group":"554","timestamp":555}];

var result = Object.values(arr.reduce((c, v) => {
  c[v.group] = c[v.group] || [];
  c[v.group].push(v);
  return c;
}, {}));


console.log(result);

With sort()

const arr = [{"id":"id4","group":"789","timestamp":111},{"id":"id1","group":"123","timestamp":222},{"id":"id3","group":"456","timestamp":333},{"id":"id5","group":"554","timestamp":555},{"id":"id2","group":"123","timestamp":999}]

var result = Object.values(
  arr
  .sort((a, b) => a.timestamp - b.timestamp)
  .reduce((c, v) => {
    c[v.group] = c[v.group] || [];
    c[v.group].push(v);
    return c;
  }, {})
);

console.log(result);

Upvotes: 3

Akrion
Akrion

Reputation: 18525

Here is another one line implementation in ES6 using Map object and reduce:

const arr = [ {id: 'id1', group: '123', timestamp: 222}, {id: 'id2', group: '123', timestamp: 999 }, {id: 'id3', group: '456', timestamp: 333 }, {id: 'id4', group: '789', timestamp: 111 }, {id: 'id5', group: '554', timestamp: 555 }, ];

const result = [...arr.reduce((r,c) => (r.set(c.group, r.has(c.group) ? 
  [...r.get(c.group), c] : [c]), r), new Map()).values()]

console.log(result)

For the sorting just prefix that reduce with a initial sort:

const arr = [ {id: 'id1', group: '123', timestamp: 222}, {id: 'id2', group: '123', timestamp: 999 }, {id: 'id3', group: '456', timestamp: 333 }, {id: 'id4', group: '789', timestamp: 111 }, {id: 'id5', group: '554', timestamp: 555 }, ];

const result = [...arr.sort((a,b) => a.timestamp - b.timestamp)
   .reduce((r,c) => (r.set(c.group, r.has(c.group) ? 
   [...r.get(c.group), c] : [c]), r), new Map()).values()]

console.log(result)

Upvotes: 0

Related Questions