Reduce method in practice - Part 1

JavaScript has a cool method called reduce. Unfortunately, understanding how reduce works and when to use it can be hard at times. So first I’ll start out with the compulsory basic examples, and then I’ll show you some more complex use cases.

What is reduce method?

reduce is a native array method in JavaScript, so, no surprise, we can apply it on arrays. The method accepts a callback function and an optional initial value as parameter.

The method will iterate over each element of the array and will apply the callback function on them, one by one. The function can have a four arguments but in real world apps we usually use less.

reduce as its name suggests reduces the number of elements in the array to a single value. Hence the general use case of this method is when we need to return the sum or a sort of summary of the elements, or there is a need to make one collection from many other. It’s worth considering reduce when one stumbles upon situations like these.

Now that we have the big picture on the method itself, let’s have a look at some basic examples.

Calculating the sum of elements

Yes, this is the base case, it’s a must-have in an article about reduce. We have an array of numbers and want to calculate the sum of these numbers. What’s happening here? We want to reduce the number of elements in the array to one value, which will happen to be the sum of the values:

const numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29];

const sum1 = numbers.reduce((sum, value) => {
  return sum + value;
}, 0); // 129

We are adding the first 10 prime numbers here. (As you might remember from school, 1 is not a prime.) reduce is passed both parameters: an anonymous callback function and the initial value, which is 0 (as the sum of the numbers is 0 before we start adding them).

The callback function itself also has two arguments. The first is called the accumulator (named sum here), and its value equals the initial value we have given as the second argument to reduce (initialValue) before we start applying the function on each element (i.e. 0). The second argument of the callback function called value refers to the individual values of the array as the method iterates over the elements. (Just too many arguments and values here.)

We will return the sum + value inside the callback function. This is what we want to do on each element of the numbers array. We want to add the numbers together.

Great, let’s see what happens when we start applying the function on the elements of the array. The first element is 2, so sum + value (0 + 2) will be equal to 2. This result will be the new value of the sum and value is assigned the next element of the array, which is 3 in our case. The callback function is applied on this element as well, so this time sum + value (2 + 3) will be 5, and this is the new value of sum, then we move on to the next element (so value will be 5), and so on. After applying the function on each element in the same way, we will get 129, and we store this result in the sum1 variable.

We can also enter a named function as the first argument to reduce. All we have to do is to define our function separately and add it to the method:

const addNumbers = (sum, value) => sum + value;
const sum2 = numbers.reduce(addNumbers, 0); // 129

We don’t call addNumbers function inside reduce, as the method does it for us when it iterates over each element.

Can I enter a number other than 0 as initialValue?

Sure, thanks for asking. We had 0 as initialValue in the previous example because we were interested in the sum of the elements in the array. If we pass in, say 31, then the sum of the elements in the array will be added to 31:

const sum3 = numbers.reduce(addNumbers, 31); // 160

Can I leave off initialValue?

Yes, in this case the first parameter of the callback function (we called it sum) will be equal to the first element of the array (2 in this case) and the second parameter of the callback function (value) will initially be the second element of the array (3).

const sum4 = numbers.reduce(addNumbers); // 129

Although in this case the end result will be the same as before, it’s safer to provide an initial value as the second argument in the callback of the reduce method.

A simple solution for calculating the sum of elements with Lodash

Lodash has a fairly straightforward method for this case:

const sum5 = _.sum(numbers); // 129

That’s it.

Lodash also has the _.reduce method, which will be discussed in the next article.

Add every second element in the array

If, for some reason, we need to add every second, third etc. element in the array, we can make use of the third (optional) parameter of the callback function, which is the index of the array element:

const sumOfEverySecondNumber = numbers.reduce((sum, value, index) => {
  if (index % 2 === 1) return sum + value;
  return sum;
}, 0); // 71

Hold on, what’s going on here?

We need the sum of every second number in the numbers array, so we should want the sum of elements with indices 1, 3, 5 etc. When the index of the actual element is odd, we add it to our accumulator (sum). If we get to even-indexed elements, we don’t want to include them in the sum, so we are not doing anything with them. We need, however, return sum, this is how reduce knows that it can move on to the next element of the array and can apply the function on it. By simply returning sum, we don’t apply any operations on the even-indexed elements.

Counting instances

Assume we have a string and need to know how many times each letter occurs in the string.

const toBeCounted = 'aaaabbccccccd';

A typical high school maths problem is to find out the number of permutations of these letters. (That is, how many different ways we can order these letters. The trick is that we can’t distinguish between the same letters.)

We will tally up the various letters and store the result in an object:

const tallyLetters = (object, letter) => {
  if (!object[letter]) object[letter] = 0;
  object[letter]++;
  return object;
};
const letters = toBeCounted
  .split('')
  .reduce(tallyLetters, {}); // { a: 4, b: 2, c: 6, d: 1 }

Let’s have a look step by step what the heck is happening here. First, we need to create an array of letters, as reduce works on arrays, so we split the string.

I defined a named function called tallyLetters, which will be called on each element inside reduce. The arguments are called object (this is the accumulator, its initial value is the empty object, this is the so called target of our operations) and letter, which is again the actual element of the array. Let’s go over the code line by line!

First, we check if the property with the actual letter exists. If not, we set its value to zero. Then we increase the value of the property by 1. The property of the actual letter will definitely exist by that time. If this is the first occurrence, its value will be set to 1 in this step (that’s why we defined 0 for the value in the first line). If the letter has already occurred before (say, we are on the third a), the if statement will be ignored and the value will jump to the next positive integer. In the end, we return object. After reduce iterates through the letters, we will get an object with the tally numbers.

Concatenating arrays

Let’s say that we have some friends who shared their favourite movies with us:

const friends = [
  {
    name: 'Jill',
    movies: ['The Last Jedi', 'The Empire Strikes Back']
  },
  {
    name: 'Jack',
    movies: ['Grown ups']
  },
  {
    name: 'Jim',
    movies: ['The Bourne Identity', 'The Man from U.N.C.L.E.']
  }
];

We want to collect all movies in one array to have a list of movies we want to watch in the next few weeks, because we appreciate our friends’ opinions on movies. In order to do that, we can type something like this:

const getMovies1 = (movies, friend) => movies.concat(friend.movies);

const allMovies = friends.reduce(getMovies1, []); // ['The Last Jedi', 'The Empire Strikes Back', 'Grown ups', 'The Bourne Identity', 'The Man from U.N.C.L.E.']

The principle is the same as before. We define an initial value, which is an empty array. We want to collect all movies in this array. This will be the initial value of movies, and then we concatenate this movies array with each array from our friends’ objects.

We can also use array spread syntax in our function:

const getMovies2 = (movies, friend) => [...movies, ...friend.movies];

The end result will be the same.

Conclusion

The reduce method is not the easiest array method to grasp. We use it when we want to have one value/object/array from the initial array of elements. The abovementioned examples are the basic building blocks of how to use reduce. More complex use cases will come in the next article.