New array grouping method for JavaScript

Grouping items in an array is something you’ve probably done many times. Either manually write a grouping function each time, or use lodashthe ‘s groupByfunction.

The good news is that JavaScript now has grouping methods, so you no longer have to do this. Object.groupByand Map.groupByThese two new methods will make grouping simpler and save us time or dependencies.

Previous practice

Let’s say you have an array of objects representing people and you want to group them by age. You can use forEacha loop like this:

const people = [
  { name : "Alice" , age : 28 },
  { name : "Bob" , age : 30 },
  { name : "Eve" , age : 28 },
];

const peopleByAge = {};

people. forEach ( ( person ) => {
   const age = person. age ;
   if (!peopleByAge[age]) {
    peopleByAge[age] = [];
  }
  peopleByAge[age]. push (person);
});
console .log ( peopleByAge);
 /*
{
  "28": [{"name":"Alice","age":28}, {"name":"Eve","age":28}],
  "30": [{"name":"Bob","age":30}]
}
*/

Or it can be used like this reduce:

const peopleByAge = people. reduce ( ( acc, person ) => {
   const age = person. age ;
   if (!acc[age]) {
    acc[age] = [];
  }
  acc[age]. push (person);
   return acc;
}, {});

Either way, the code is slightly clunky. You always have to check if the grouping key exists on the object, and if it doesn’t exist, create it with an empty array. Then push the item into the array.

Use Object.groupBy

With the new Object.groupBymethod, you can get the result like this:

const peopleByAge = Object . groupBy (people, ( person ) => person. age );

Much simpler! However, there are some things to note.

Object.groupByReturns an empty prototype object. This means that the object does not inherit Object.prototypeany properties from . This is good because it means you won’t accidentally override Object.prototypeany properties on , but it also means that the object doesn’t have any methods you might expect, like hasOwnPropertyor toString.

const peopleByAge = Object . groupBy (people, ( person ) => person. age );
 console . log (peopleByAge. hasOwnProperty ( "28" ));
 // TypeError: peopleByAge.hasOwnProperty is not a function

The callback function passed to Object.groupByshould return a string or Symbol. If something else is returned, it will be coerced to a string.

In our example, we have been returning a number age, but are being coerced to a string in the result. Nonetheless, you can still access properties using numbers, since using square bracket notation also forces the parameter to be a string.

console . log (peopleByAge[ 28 ]);
 // => [{"name":"Alice","age":28}, {"name":"Eve","age":28}] 
console . log (peopleByAge[ "28" ]);
 // => [{"name":"Alice","age":28}, {"name":"Eve","age":28}]

Use Map.groupBy

The functionality of is almost the same as except Mapthat it returns . This means you can use all common functions. This also means that you can return any type of value from the callback function.Map.groupByObject.groupByMap

const ceo = { name : "Jamie" , age : 40 , reportsTo : null };
 const manager = { name : "Alice" , age : 28 , reportsTo : ceo };

const people = [
  ceo,
  manager,
  { name : "Bob" , age : 30 , reportsTo : manager },
  { name : "Eve" , age : 28 , reportsTo : ceo },
];

const peopleByManager = Map . groupBy (people, ( person ) => person . reportsTo );

In this example, we group people by who they report to. Note that to retrieve items by object from this Map, the objects must have the same reference.

peopleByManager. get (ceo);
 // => [{ name: "Alice", age: 28, reportsTo: ceo }, { name: "Eve", age: 28, reportsTo: ceo }] 
peopleByManager. get ({ name : "Jamie" , age : 40 , reportsTo : null });
 // => undefined

In the example above, the second line uses an ceoobject that looks like an object, but it’s not the same object, so it doesn’t return anything from Mapit . To successfully get items Mapfrom , make sure you keep a reference to the object you want to use as a key.

When will it be available

These two groupBymethods are part of the TC39 proposal and are currently in phase three. This means it has a good chance of becoming a standard, so there are some implementations.

Chrome version 117 has just launched support for both methods, and Firefox version 119 has also released support for both methods. Safari implements these methods under different names and I’m sure they will be updated soon. Now that these methods appear in the Chrome browser, it means that they have been implemented in V8, so the next time V8 is updated, these methods will also appear in Node.

Why use static methods

You may ask, why should it be implemented in the form of Object.groupByinstead of Array.prototype.groupBy? According to the proposal, a library had monkey-patched an incompatible groupBymethod . Array.prototypeBackward compatibility is very important when considering new APIs. Array.prototype.flattenThis was highlighted a few years ago during an event known as SmooshGate when trying to implement it .

Fortunately, using static methods seems to be better for future scalability. When the Record and Tuples proposals are implemented, we can add a Record.groupBy method for grouping arrays into immutable records.

Summarize

Grouping projects is obviously an important job for us developers. Currently, the number of downloads from npm per week lodash.groupByranges from 1.5 million to 2 million. It’s great to see JavaScript filling in these gaps and making our jobs easier.

Download Chrome 117 now and try these new methods for yourself.