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.

  1. The first element is 'ashley'.
    1. 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.
    2. The current return object is empty, so the key a is added, with the value ['ashley'].
    3. The current return object is {a: ['ashley']}.
  2. The second element is 'james'.
    1. The key associated with 'james' is the result of the callback function, j.
    2. The current return object is {a: ['ashley']}, which does not have key j. It's added, with the value ['james'].
    3. The current return object is {a: ['ashley'], j: ['james']}.
  3. The third element is jenn.
    1. The key associated with 'jenn' is the result of the callback function, j.
    2. The current return object is {a: ['ashley'], j: ['james']}. The key j is already present so the value jenn is added to the array.
    3. The current return object is {a: ['ashley'], j: ['james', 'jenn']}.
  4. The final element is 'rick' which will recieve the same treatment as our second element, but with the key r.

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!