JavaScriptT2 Dark_1200x303

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.

Britney Spears looking confused

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.

Counting Marbles

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/.

Taking it a Step Further: Gemcutting 101

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/.

Wrapping up

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_2020
About the Author

Marina Mosti

Marina Mosti is a full-stack web developer with over 13 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.

She currently holds a position as Lead FE Developer at VoiceThread, and she is the author of the FormVueLatte library as well as a member of the Vuelidate team. In her spare time, she enjoys playing bass, drums, and videogames.

Related Posts

Comments

Comments are disabled in preview mode.