Grouping items in an array is something you’ve probably done many times. Either manually write a grouping function each time, or use lodash
the ‘s groupBy
function.
The good news is that JavaScript now has grouping methods, so you no longer have to do this. Object.groupBy
and Map.groupBy
These two new methods will make grouping simpler and save us time or dependencies.
Contents
Previous practice
Let’s say you have an array of objects representing people and you want to group them by age. You can use forEach
a 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.groupBy
method, 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.groupBy
Returns an empty prototype object. This means that the object does not inherit Object.prototype
any properties from . This is good because it means you won’t accidentally override Object.prototype
any properties on , but it also means that the object doesn’t have any methods you might expect, like hasOwnProperty
or 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.groupBy
should 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 Map
that 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.groupBy
Object.groupBy
Map
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 ceo
object that looks like an object, but it’s not the same object, so it doesn’t return anything from Map
it . To successfully get items Map
from , make sure you keep a reference to the object you want to use as a key.
When will it be available
These two groupBy
methods 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.groupBy
instead of Array.prototype.groupBy
? According to the proposal, a library had monkey-patched an incompatible groupBy
method . Array.prototype
Backward compatibility is very important when considering new APIs. Array.prototype.flatten
This 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.groupB
y 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.groupBy
ranges 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.