Array reduce is a pre-built function in JavaScript notorious for confusing new developers. Let’s break it down together.
JavaScript is filled with amazing pre-built functions for manipulating objects and arrays. Have you yet mastered some like Array.map
or Array.filter
? What about the elusive Array.reduce
?
Array reduce in particular can be a bit convoluted for new developers. I remember when I first started learning the array manipulator functions. Filter
, sure—that made perfect sense. Map
was a little more confusing, but after following through some example code it clicked. But reduce? Boi o boi. :D
Hitting up MDN, you are slapped by this description.
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
Moving on through the doc you are dumped into terms like accumulator
without any explanation—no no. So without further ado, let’s walk through this one together with examples that make little to no sense but actually help you learn stuff.
Imagine you have a bag where you store your marbles, say const marbles = []
. At first your bag is empty, but you want to store some marbles in it, so you marbles.push(3)
some marbles into it. This bag has many pockets inside of it, so after adding some marbles and looking inside you see [3, 5, 1]
.
The objective is now to find a way to count them. We want to reduce
this whole bag (array) into a single value—a count
of our marbles.
When you start counting your marbles, you’ll make a pile to accumulate
or group the ones you already counted. That way we can ensure we don’t count the same pocket twice.
Let’s try to put this whole explanation to code.
const marbles = [] // Our bag
marbles.push(3) // Add marbles to the inner pockets
marbles.push(5)
marbles.push(1)
console.log(marbles) // [3, 5, 1]
// How many marbles I gots?
// Count will be our group/accumulate of marbles
// The first time it runs it will be already = 3. We like being efficient here,
// so we start with the first pocket already in the count
const marbleCount = marbles.reduce((count, value, index) => {
console.log(`Current index: ${index}`)
// Output 1 on first pass, then 2. Notice we skip 0 here.
const totalSoFar = count + value
console.log(`Adding ${value} marbles to our count: ${count}. Now we gots: ${totalSoFar}`)
/*
Fist pass: Adding 5 marbles to our count: 3. Now we gots: 8
Second pass: Adding 1 marbles to our count: 8. Now we gots: 9
*/
// Every time we count, we have to return
return totalSoFar
})
console.log(`Marble count: ${marbleCount}`)
Let’s go through this slowly, because it can be a little confusing. First, we create the array
that will hold our marbles, and we add them to different indexes by using array push
.
We call marbles.reduce
, which takes a function
as a first parameter. This function takes three params: the count
(also called accumulator
), the value
, and the index
. This last one is optional.
The count
will be the number of marbles we have counted SO FAR. That means it will increment each time we go through the function. It will start out with the value of marbles[0]
, so 3
.
The value
will hold the value of the next index in the loop. The reduce
function will go through each one EXCEPT for index [0]—remember, we get that for free on the first count
already.
The index
is the current index for the array being reduced. Remember how we just mentioned that index 0
will be skipped though? This means the first pass, value will give us 5
and index
will be 1
. The second pass will give us a value
of 1
, and the index
2.
Run this code and check out the console, even play around with the console logs and the values, add more marbles to the array
. Once you see it in motion, it will become clearer. Here’s a fiddle for you try it out:
https://jsfiddle.net/jx7aw3rz/4/.
Ok yeah, reduce
is really nice at running a fixed set of values through a simple math function, but I’m guessing you want to learn how to use it with more interesting things like processing a batch of data, or even filtering it out too.
Let’s pretend we’re building a game, some sort of RPG (Role Playing Game), and we want to create a reduce
function that takes all of our player’s gems and cuts them.
Before anything though, we’re going to do a little setup with a couple of variables and a function.
const gems = [
{
type: 'Ruby',
cut: false,
value: 100,
requiredSkill: 1
},
{
type: 'Sapphire',
cut: false,
value: 200,
requiredSkill: 2
},
{
type: 'Opal',
cut: false,
value: 250,
requiredSkill: 8
},
{
type: 'Diamond',
cut: false,
value: 300,
requiredSkill: 5
}
]
const gemcuttingSkill = 4
const cutGem = gem => {
if (gem.requiredSkill > gemcuttingSkill) return
gem.value = gem.value * 3
gem.cut = true
}
Our gems
array will be an array of objects, and each one will be a gem object.
Next, gemcuttingSkill
will be a numeric value that will indicate the skill that the user needs to cut a gem—if the gem has a very high requiredSkill
, the user won’t be able to cut it.
Finally, we have a function called cutGem
. It takes a single param, a gem
, and it attempts to cut it. First, however, it checks against the user’s skill. Notice also that the value
of a gem increases by 3 when it’s cut!
Let’s write the reduce function that will use the cutGem
function on all of our gems. We are going to make one small exception though! We will not cut any Diamonds
—they’re too rare to be cut. Those go to the auction house.
const cutGems = gems.reduce((acc, gem, index) => {
console.log('Processing gem: ', gem)
console.log('With index', index)
if (gem.type === 'Diamond') {
// Leave Diamond uncut, and dont add it to the accumulator
return acc
}
cutGem(gem)
if (!gem.cut) {
// The player wasnt able to cut it
return acc
}
acc.push(gem)
return acc
}, [])
console.log(cutGems)
We call .reduce
on our gems array, and we name our accumulator
parameter acc
. This is a common convention that you may find in some articles and resources.
The second parameter that the function receives is the gem
, because we are looping through the array.
Now I want you to take a quick look at the end of the function block, right after return acc
—don’t worry, we’ll come back to the function in a minute. Do you see that empty array that we are passing as a second parameter to the reduce
call? That’s the default
or starting
value for the accumulator
.
Earlier, when we were looking at the example with the marbles, I told you that the first element had already been added to the accumulator for us for free, but what if we want to provide an initial value for our accumulator and manipulate it inside the reduce
function?
The solution is to pass in a second optional argument to array.reduce
that will be the default value. So the first time that the function we are providing for gems.reduce
is called, acc
will have a value of []
, and the gem
that we’re getting is going to be the one at index 0
!
Ok, coming back to the contents of the function. First we are going to log a couple of things so that we can see in the output of the console how reduce is working.
Next, we add an if
statement—we will check to see if the gem
is of type Diamond
. If it is, we will return the acc
itself without modifying it, because we don’t want to add this gem to it. Keep in mind that you always
have to return the acc
inside the reduce
function, even if you didn’t modify it.
Afterwards, we call cutGem
with the current gem
as a parameter. Our function will execute and cut the gem if the player has the required skill level. Then we will check to see if the gem was actually cut in the next if
statement. If it hasn’t, we’re going to return the acc
in this case too.
Finally, we push the cut gem
into the acc
array and return it.
This whole process will be executed for our bag of gems, one at a time, and we will get back into our const cutGem
an array of the gems that were cut.
Let’s take it a step further and use array.reduce
once again to calculate the value of the cut gems, if there are any.
const gemValue = gems.reduce((acc, gem) => {
return acc + gem.value
}, 0)
console.log(`The cut gems have a value of ${gemValue} gold`)
Once again we have a reduce
call on our gems
array—but pay close attention to the default value. This time we’re setting it as 0.
If we skipped this tiny detail and omitted the default value for the acc
, then our reduce function would receive the whole gem
as the value of the acc
, which is not what we want. We want to deal with numbers only.
Inside the function, we go through each cut gem
, and we return the current value of the acc
plus the value of the current gem. So each time we go through the loop, the gem.value
will be added to the accumulated value, and so on.
Here’s a fiddle for you to execute and test this code:
https://jsfiddle.net/m7agkudx/.
Array.reduce
is one of those functions that has a name that sometimes isn’t very descriptive of what it does. The flexibility that we get for processing array items with this function is fantastic, but comes at a cost of a higher learning curve.
Hopefully with these couple of silly examples you are able to dive right in and start writing your own reduce
functions!
As always, thanks for reading and share with me your thoughts on Twitter at: @marinamosti.
P.S. All hail the magical avocado! 🥑
P.P.S. ❤️🔥🐶☠️
Marina Mosti is a frontend web developer with over 18 years of experience in the field. She enjoys mentoring other women on JavaScript and her favorite framework, Vue, as well as writing articles and tutorials for the community. In her spare time, she enjoys playing bass, drums and video games.