Array reduce is used to accumulate a new value by applying a function that you provide to each item in your source array. Reduce behaves differently based on whether or not you provide an initial value - we cover this in depth in this lesson and provide some practical use cases.
If we wanted to create a sum of all of these numbers, it would take this items array, call reduce on it, pass in a call back function that has two parameters, the previous value and the current item that we're looking at.
We return the result of adding the previous value and the current value together. Log it to the console, and we get the correct result. The interesting thing about this example is that this callback here is only actually executed four times even though we have five items in this array.
This happens because for every call, we need access to both the previous value and the current one. For the very first call, we don't have a previous value. What it does, it takes the first item of the array, provides that as the previous value, and then it takes the second item of the array and provides that as the current.
Let's sketch this out. The first time our callback runs, we need a previous and a current value. We'll take those two there, and we'll call our function with one and two. This now represents this. You can see that on the very first call, you're going to be adding one and two together and returning that result. This returns three.
The second time it's called, we now have a previous value here. We pass that in along with the next item in the array which happens to be a three. This is the second function call. It's the result of the previous one along with the next item in the array. This returns six.
The same thing repeats. We take the result of the previous call, pass it in there, then grab the next item in the array. That's four. This returns 10. It happens one final time. The fourth function call will get 10 and the item in the array, which is five. This returns 15.
Because this is the last item of the array, this value will be returned, and it will be set to this constant sum. To prove this, we can provide a small piece of logging inside this function instead. If we break this out, we'll return the same expression. Before we do that, we'll log out the previous value and then the current item that we're looking at.
When we log that, you can see we just get the four function calls with the really part is that for the first one, it's taking two items from the source array. For all the other calls, it's using the return result of each call along with the next item in the array.
As well as your callback, reduce actually takes a second argument. It's optional, but if you provide it here, I'm going to use zero. It will actually use this as the previous value. We often talk about this as the initial value or the seed.
Either way, this is saying for the very first function call, don't use two items from my array. Instead, use this value as the previous and then use the first item in my array. This is very different because now this function body will be executed five times, once for each item.
Which we can see if we log this to the console, we get that extra function call where we have zero provided as the previous value which is coming from here. Now, onto some practical examples. Here we have an array of albums. It's two objects that each represent an individual album. You have a title, images property.
Images is simply an array of file paths. If we wanted to count how many images we had in total, that's a great use case for reduce. We can say that num images equal to albums.reduce. We provide a function. We have our previous value and the current album.
In our callback, we're going to return the previous value plus the length of the current album's images array. Before I run this, can you spot the mistake? We haven't provided an initial value here, so on the first call, previous is actually going to be equal to this object, and album here will be equal to the second object.
We'll be trying to add an object to the length of the second images array. The whole thing is just incorrect. We need to provide a zero here as we're expecting a number to be returned. This will ensure that previous here is equal to zero first.
That this gets executed twice so that we can look at the length of this array, add it to the length of that array, and return it. Now, if I log this, you can see we get five. If I remove this initial value, we get this strange result here.
Reduce is not limited to just adding numbers together. You can use it to accumulate all types of values, like arrays for example. Let's say you wanted to create a new array from this that just contained all of the images in a single flat array. Let's walk through it.
First we'll just change this so it makes more sense. We'll just say it's images. Our initial value does not want to be a number this time. It actually wants to be an empty array. For the first call, previous will be equal to this empty array.
What do we want to do? We know that when it's called the second time, we're going to expect an array with these items in it. If we return previous, which is an empty array at this point, we'll concat onto it the current album's images.
If you pass an array to the concat method, it will pull out each of the values and add them individually to the array that you are concatting onto. After the first call, we'll end up with an array containing these two images. That will be passed into the next function call as the previous value onto which we concat these three.
The end result if we log it to the console is a single flat array containing all of the images. We can also use reduce to build up objects. Let's say you have the following data, an array of users. Each object has an ID and a name.
Let's say that within your application, you wish that you can access your users in the following way, users, ID, and then name for example. This wouldn't currently work because we're dealing with an array, so you would have to filter it first looking for the ID, and then you have to access the first result of the filtered array. It can be tricky.
What you really want is the data in this format where users is actually an object, and you want the ID to be the key of each user. This will allow you to easily access each user if you know their ID without having to filter. We just need to transform this original array using reduce.
To do that, we'll say that new users is equal to users.reduce and then the value of building up is an object. We'll just call it object. For each call, we'll have access to the user. We need to pass in a new object as the initial value so that we can add our users to it.
On the very first call, object here will be equal to this, but we get to look at this item. What we can do is use a square bracket syntax to add a new element to this object. We can use the user ID. We'll set that equal to the user. Then we'll return the object.
Note that we don't put a return statement here because setting a value on an object does not return the object on the left hand side. We wouldn't be able to accumulate the value. We just set a new property on the object here equal to the user and return it.
Now, if we log this to the console, you can see that we do have a new object. It has two keys, 01 and 02, and they are equal to the user object. The important part is that now if we want to access the user with an ID of 02 for example, we could just go new users 02 and access the name property, and we get the result we expect.