Can't undestand the reduce() function

I am not understanding the reduce() function. MDN and W3 schools wasn’t helpful for me. Can anyone help me by explaining the reduce() function in simple language.

It would help if you explained what parts you do understand and what parts confuse you.

I can’t understand the parameters

So, as it happens, I tend to get verbose. But in this case, I think it might be excusable.

Reduce is the Swiss Army knife of the array methods. With it, you can recreate map(), findIndex(), filter(), sort() or any combination of the other methods. But because it contains all that power, it requires us to pay a little more attention.

Here’s a basic arr.reduce() call:

const arr = ["Bob", "Alice", "Janelle", "Artemis", "Artie"];

// We'll use reduce to get the total letters in all those names.
const totalLetters = arr.reduce(function( accumulator, arrayMember){
  accumulator = accumulator + arrayMember.length;
 /* This is important - whatever we return inside here  becomes the new
  *   value for accumulator on the next pass!
  */
  return accumulator; 
}, 0 /* our accumulator starts at zero */);

That’s a relatively simple example, and it does a small thing. And it only uses two of the reduce callback’s parameters. So arr.reduce() runs that inner function over each member of the array, and the first parameter simply keeps being changed for each pass, and the second parameter is the actual array member we’re working with. Each time, we need to return something, as that will be our new accumulator for the next pass through.

We have more parameters for the callback function, though. Here they all are, broken down:

  • accumulator: this is a value that gets passed along on each iteration through the reduce function, and it is the final return value of that inner function.
  • arrayItem: this is the current array member we’re working with. We can use this to change something about that accumulator, if we like.
  • arrayIndex: this is the current array member’s index in the original array. This, too, can be useful information, especially in combination with the next one.
  • originalArray: this is the original array, which we are not and should not modify. We can reference it, but we shouldn’t change it.

Those are all the parameters, and we can use any of them we might need. Here’s a little more complex example, using all but the last, and returning something different: This time, we won’t simply return a number. We can create whatever we like. So let’s get creative. We’ll turn that array into an object, and we’ll create custom object with two properties: evenTotal and oddTotal. So for all the members of the array with an even index value, we’ll add that to the evenTotal property, but if it isn’t, we’ll add the string length to the oddTotal instead.

const arr = ["Bob", "Alice", "Janelle", "Artemis", "Artie"];
// and I'm going to create a handy function that returns true if a number is even
//  Basically, if the modulo of a number/2 is zero, we return true.
const isEven = (num) => num%2===0;

const finalObject = arr.reduce(function(accumulator, arrayMember, arrayIndex){
  if(isEven(arrayIndex){
    accumulator.evenTotal += arrayMember.length;
  } else {
    accumulator.oddTotal += arrayMember.length;
  }
  // And remember, we *always* need to return something for the accumulator
  return accumulator;
}, {
  evenTotal: 0,
  oddTotal: 0
} /* we start accumulator with a basic object here, with zeroed properties */

console.log(`The length of all the even-numbered index values is ${finalObject.evenTotal}, and all the odds is ${finalObject.oddTotal}.`);

Bear in mind, though, the reduce function itself only takes two parameters: a callback function, and a starting value for the accumulator. All that stuff above? That’s the parameters the callback function can be watching for. Those are the confusing bit, to my mind.

The takeaway? The bullet list above explains each of the parameters our inner function can take. And reduce isn’t just about “total some number”. It can do many things. It can create a new array of the same or different length, it can create an array of objects, it can create an object, or a string, or whatever we want.

Don’t do this – a reducer function shouldn’t have state or even look like it does. It might look more familiar to people writing loops, but it’ll screw you the moment you start updating a mutable structure in your reducer. Besides, a one-expression arrow function is a lot pithier.

Easiest way to think of reduce is you have a value, and a list of items you want to “update” that value with, step by step. The rest is as explained right above, but the key thing is you’re always updating something with a series of steps, where the function defines what that step is, and the array is the value you use for each step.

Basically, if you find yourself writing code like this:

let foo = something
for (item of somearray) {
  foo = doSomethingWith(foo, item)
}

Then you pretty much have a reduce. You’re turning an array of values into one thing (that’s why it’s called “reduce”). The reduce() method just lets you do that sort of thing without actually changing foo. I’ll leave it as an exercise to come up with the equivalent reduce() expression for that loop.

Also, forget the third and fourth arguments to the reducer function even exist. You pretty much never need them, and other languages with a reduce (sometimes called “fold”) don’t even have them.

Finally, there’s this always entertaining guy who explains things really well (he does keep accidentally saying “reject” instead of “reduce” though):

1 Like

@chuckadams , The video is great. Thank you.