The power of .reduce()

There are a number of common reduce operations that you'll need to use in one way or another as a JS developer.

Let's start with just a simple array of numbers

const numbers = [1, 2, 3, 4, 5, 6];

And let's say that we want to, for each of these numbers, multiply them by 2 and return an array with those doubled values.

So to get us started, take a second and see if you can write out how we would use the reduce method to solve this problem. You don't need to write out the entire solution, just how we would set it up using the reduce method. So make sure to include it's two arguments...

So first we need to pass in its function, called the reducer, which applies the operation to each array element, and it accepts an accumulator as well as the array element we're iterating over. In this case, instead of writing out the whole word accumulator, we can just abbreviate it as acc, with each element being called num.

numbers.reduce((acc, num) => {});

And now we need to provide the initial value, and to figure out what this needs to be, remember that it's determined by the data type that we want the result to be. We said that we wanted an array back, so the initial value, which is the value of our accumulator on the first iteration, should be an empty array:

numbers.reduce((acc, num) => {}, []);

So now to double each element we can take the number element, multiply it by two and push it onto the accumulator array. So we're creating a brand new array here from scratch, so as a result, we can use whatever array method we like--one that mutates the array or doesn't mutate it. It doesn't matter, because numbers itself will not be affected, so let's push that result onto the end:

numbers.reduce((acc, num) => {
  acc.push(num * 2);
}, []);

And finally, do you remember what the last thing that we need to do is to be able to get our returned array from reduce...?

We need to always make sure to return our accumulator at the end. And after that we can console log the result that we get along with the original numbers array:

const doubledNumbers = numbers.reduce((acc, num) => {
  acc.push(num * 2);
  return acc;
}, []);
console.log("Doubled numbers", doubledNumbers);
console.log("Numbers", numbers);

We see that we have created a new array with each of the numbers multiplied by 2 and that we have not mutated the original array.

And this manner of transforming arrays has become so common that it has been made a standard feature of arrays. Can you guess what it is? It's the .map() array method. We've mapped the numbers array into a new set of data.

And just to refresh your memory, we can use map to do the same thing, like so:

const mappedNumbers = numbers.map((num) => num * 2);
console.log("mapped numbers", mappedNumbers);

So be aware that this is the exact same operation. The map function is really just a shorthand for what was already possible with reduce.

Let's take a look at another example:

Say that we want to get all of the numbers from the original array that are greater than 3 and put them in their own array. In other words, to get a subset of an array based on a condition. How would we do that with reduce?

If you're really feeling confident with reduce, go ahead and try to do it on your own, otherwise do it along with me.

Once again we'll set up our reducer and our initial value as before. It will be exactly the same initial value since again we're getting back an array:

numbers.reduce((acc, num) => {}, []);

Then we want to check each number that we're iterating over, we want to see if it is greater than 3. If so, to put in on the accumulator and therefore to have it added to our returned array from the reducer. So we'll use an if statement for that comparison and again, to add it to the resulting array, we can use the push method, and once again, make sure to return the acc at the end for each iteration:

numbers.reduce((acc, num) => {
  if (num > 3) {
    acc.push(num);
  }

  return acc;
}, []);

And finally, let's put the resulting array in a variable which we'll log called greaterNumbers:

const greaterNumbers = numbers.reduce((acc, num) => {
  if (num > 3) {
    acc.push(num);
  }

  return acc;
}, []);
console.log("Greater numbers", greaterNumbers);

And as we would expect, we get an array with 4, 5, and 6.

So what about this operation? At this point, you may be catching on--this is just our filter method. Where we're able to provide a condition or multiple conditions and get returned a new subarray for which the condition was true:

Let's see how filter can be used to solve this same problem:

const filteredNumbers = numbers.filter((num) => num > 3);
console.log("filtered numbers", filteredNumbers);

Again, filter is doing the exact same thing as reduce. It was added more or less as a convenience for JS developers, where as you can see there is a significant amount more code to do the same thing with reduce.

However, note that using some concise JS patterns that we've picked up already, we can make reduce a lot easier to work with. In our last problem, for example, we can really clean things up with a ternary.

We can swap out the if statement, for the then condition, return acc.push(num) and for the else case, just return the acc. So we can write this all on one line:

const greaterNumbers = numbers.reduce(
  (acc, num) => (num > 3 ? acc.push(num) : acc),
  []
);

However note that push doesn't return the updated array, so we can't provide that as the accumulator. Instead, we need to use concat, since it does return the updated array:

const greaterNumbers = numbers.reduce(
  (acc, num) => (num > 3 ? acc.concat(num) : acc),
  []
);
console.log("Greater numbers", greaterNumbers);

And with that, we get the same result as before.

Review

So in this video, I've hoped to convey to you two important points:

  1. The real power of reduce. In fact, virtually every major array method can be created with reduce, and--
  2. The reason why this is possible is because ultimately the majority of array methods that you use and that we talked about first in this section can be thought of as reductions. That's because each of them take an array and they transform that array into something else.

Challenge

So start working more with reduce, try to implement some custom reduce functions on your own, for example, if you think you know reduce now, try to create the some or the every method using reduce, just like we did here with map and filter.

The better you get with reduce, the better you get with working with arrays in general, possibly the most flexible and convenient data structure there is in JavaScript.