It's that time again! TC39 has moved the array grouping proposal to Stage 3. That means it's one step away from being formally adopted and becoming a part of the JavaScript language.
So let's learn what it is and how it works.
Arrays get a lot of love
When you think about arrays, a number of different functions may come to mind -- map
, filter
, reduce
, flat
, etc. Arrays have become a very powerful data structure in JavaScript and these utilities make working with them even more efficient.
The latest proposal introduces yet another function, groupBy
. If you're familiar with lodash you may recognize it. In fact, it's often cited as one of the main reasons people still add lodash to their projects.
Well, no more! (probably)
What does it do?
groupBy
takes a function that gets called for each element in the array. That function has three params. The first is the value of the element. The second is the index of that element. The third is the array itself.
Let's show an example. Note that we're not using all of the possible params, so we won't include them.
const arr = ['ashley', 'james', 'jenn', 'rick']const byFirstLetter = arr.groupBy(name => {return name.charAt(0)})// byFirstLetter is { a: ['ashley'], j: ['james', 'jenn'], r: ['rick] }
Wait, what?! 🤯 How does this all work?
What is happening?
The first thing to understand is that groupBy
returns a object with keys that have array values.
As groupBy
iterates through each element in the array it's looking for a key. That key tells it which array to add the element to. Let's walk through the above example step by step.
- The first element is
'ashley'
.- The key associated with
'ashley'
is the result of the callback function. The callback function returns the first character of the string, in this case,a
. - The current return object is empty, so the key
a
is added, with the value['ashley']
. - The current return object is
{a: ['ashley']}
.
- The key associated with
- The second element is
'james'
.- The key associated with
'james'
is the result of the callback function,j
. - The current return object is
{a: ['ashley']}
, which does not have keyj
. It's added, with the value['james']
. - The current return object is
{a: ['ashley'], j: ['james']}
.
- The key associated with
- The third element is
jenn
.- The key associated with
'jenn'
is the result of the callback function,j
. - The current return object is
{a: ['ashley'], j: ['james']}
. The keyj
is already present so the valuejenn
is added to the array. - The current return object is
{a: ['ashley'], j: ['james', 'jenn']}
.
- The key associated with
- The final element is
'rick'
which will recieve the same treatment as our second element, but with the keyr
.
The result is the object { a: ['ashley'], j: ['james', 'jenn'], r: ['rick] }
.
Isn't that a Map?
If you look at the object groupBy
returns it looks a lot like a Map. Unfortunately, it isn't one and that means some of the nice helper functions Maps come with aren't available.
Luckily, there is a second proposed function called groupByToMap
. It does the same thing as groupBy
, but returns a Map
!
Map
has a forEach
function, so you could do something like this.
const arr = [{ name: 'ashley', score: 12 },{ name: 'jenn', score: 22 },{ name: 'ashley', score: 20 },{ name: 'rick', score: 15 },{ name: 'jenn', score: 3 },]arr.groupByToMap(obj => {return obj.name}).forEach((personArr, name) => {const total = personArr.reduce((sum, person) => {return sum + person.score}, 0)console.log(`${name}'s total is ${total}`)})
This function sums up the total points for each user and logs them out. It's useful, but certainly not the only way to achieve this functionality.
All those params
You may be wondering about the three params the callback takes and the fact that our examples only use the first one. That's ok! The goal of the callback is to return a key for each value. The logic you use to return that key could be based on the value itself, the index, or even a comparison to the original array.
What do you think?
Would you use groupBy
or groupByToMap
? If it helps remove a dependency like lodash then I'm all for it!