JavaScript reducer – A simple, yet powerful array method
Know the ins and outs of this basic JavaScript array function
Javascript reducer is one of the most useful array methods that should be in a developer’s arsenal. Introduced in ES5, it’s somewhat similar to for…each and map methods that are used with arrays but improves on their performance and simplicity in specific situations.
reduce method executes a callback function that we provide on each element stored in an array and outputs the final value the operation generates. It’s a cleaner way of iterating over and processing the data stored in an array.
Currently, it’s supported by all of the major browser versions and is available in Node.js from version 10.0 upwards.
Today, we are going to explore this reduce method. We’ll go through the how-to and when-to guide of using the Javascript reducer in different scenarios.
Let’s get started then!
Javascript reduce method parameters
reduce method accepts two arguments: a reducer function for the array that is used as a callback and an optional initialValue
argument. The reducer function takes four arguments: accumulator
, currentValue
, currentIndex
, and array
.
arr.reduce(callback( accumulator, currentValue, [, index[, array]] )[, initialValue])
An example of Javascript array reduce method in action:
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => accumulator + currentValue);
This reduce method does the same job as the following for…each loop but with lesser lines of code.
const array = [0, 1, 2, 3, 4];
let total = 0
array.forEach(num => {
total += num
});
How does the reduce method achieve it using these parameters?
The value returned by the reducer function is assigned to the accumulator
variable. In each iteration through the array items, the accumulator’s value is updated to the returned result. At the end of the iteration, the final value of the accumulator is returned as the output of the reduce
function.
If an initialValue
argument is passed, the first time the reducer function is executed, the accumulator
will be equal to initialValue
and the currentValue
will be equal to the first element stored in the array. If an initialValue is not passed, the accumulator
will be equal to the first value of the array and currentValue
will be equal to the second.
Let’s see how the values of each of these parameters change every time the callback function is called in the following example. Here, we are not passing an initialValue
argument.
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => accumulator + currentValue);
accumulator | currentValue | currentIndex | array | return value | |
---|---|---|---|---|---|
1 | 0 | 1 | 0 | [0, 1, 2, 3, 4] | 1 |
2 | 1 | 2 | 1 | [0, 1, 2, 3, 4] | 3 |
3 | 3 | 3 | 2 | [0, 1, 2, 3, 4] | 6 |
4 | 6 | 4 | 3 | [0, 1, 2, 3, 4] | 10 |
The final output of this function is 10
.
Next, let’s see how it works when an initialValue
is passed.
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => accumulator + currentValue, 12);
accumulator | currentValue | currentIndex | array | return value | |
---|---|---|---|---|---|
1 | 12 | 0 | 0 | [0, 1, 2, 3, 4] | 12 |
2 | 12 | 1 | 1 | [0, 1, 2, 3, 4] | 13 |
3 | 13 | 2 | 2 | [0, 1, 2, 3, 4] | 15 |
4 | 15 | 3 | 3 | [0, 1, 2, 3, 4] | 18 |
5 | 18 | 4 | 4 | [0, 1, 2, 3, 4] | 22 |
This function outputs the value 22
.
When to use Javascript reducer
Reduce method provides a unique way to iterate through items in an array and process them. So what are the situations we can benefit from this uniqueness?
Calculate the sum of values in an array
This is similar to what we did in previous examples. The only difference is we have to pass 0 for the initialValue
parameter.
let total = [34, 12, 143, 13, 76].reduce((accumulator, currentValue) => accumulator + currentValue, 0);
Flatten an array
If we have an array of arrays, we can use the reduce method to flatten it and create a single array without nested arrays.
let array = [[0, 1], [2, 3], [4, 5], [5, 6]];
let flattenedArray = array.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
We pass an empty array as the initial value so the items in the first array are concatenated with it to create a flattened array.
If the first array has more than one level of nested arrays, we can recursively call the reduce function to flatten and then concatenate them with the final array.
let nestedArray = [[1, [2, 3]], [4, 5], [[6, 7], [8, 9]]];
function flattenArray(nestedArray) {
return nestedArray.reduce(
(accumulator, currentValue) =>
accumulator.concat(
Array.isArray(currentValue) ? flattenArray(currentValue) : currentValue
),
[]);
}
let flattenedArray = flattenArray(nestedArray);
If the current value accepted by the callback is an array, as verified using the isArray method, we recursively call the flattenArray function on it. If the current value is not an array, we simply concatenate the value with the final flattened array.
Grouping an array of objects by a property
Assume that we have an array of country objects. We want to group each country in the array according to their continents. We can use the reduce method for this task.
let countries = [
{name: "Germany", continent: "Europe"},
{name: "Brazil", continent: "South America"},
{name: "India", continent: "Asia"},
{name: "France", continent: "Europe"},
{name: "South Korea", continent: "Asia"},
]
let groupedCountries = countries.reduce(
(groupedCountries, country) => {
if (!groupedCountries[country.continent]){
groupedCountries[country.continent] = []
}
groupedCountries[country.continent].push(country)
return groupedCountries
},
{});
Inside the callback function, we create a new key for each continent that is not in the groupedCountries map and assign an empty array as its value. Then we push each country object to the array stored by their respective continents.
Using reduce() in place of filter().map()
In Javascript, we use the filter method to filter items stored in an array using a callback. We use the map method to create a new array using the old array using the logic passed inside a callback. Sometimes we have to use these two methods, one after the other to create a new array with the results we filter using some conditions.
Instead of using two array methods, you can use the Javascript array reduce method to complete the same task. It will reduce the completion time because now you only iterate through the array only once, not twice.
For example, let’s take the following scenario where we want to create an array of square roots of numbers greater than 30.
let numbers = [3, 21, 34, 121, 553, 12, 53, 5, 42, 11];
let newArray = numbers.filter(number => number > 30).map(number => Math.sqrt(number));
The same scenario implemented using reduce looks like this.
let numbers = [3, 21, 34, 121, 553, 12, 53, 5, 42, 11];
let newArray = numbers.reduce((accumulator, current) => {
if (current > 30) {
accumulator.push(Math.sqrt(current))
}
return accumulator
}, []);
Inside the callback, we simply check if the number is greater than 30 and add its square root to the accumulator array. You have to pass an empty array as the initial value to get this result.
Build your own reducer
In this section, we are going to implement the reducer function on our own to see how things work under the hood. This will give you a better idea of when to use the Javascript reducer for optimal performance in your program.
Array.prototype.reduceConceptual = function(callback, initial) {
if (!this) {
throw Error('Array.prototype.forEach called on null or undefined');
}
if (!callback || typeof callback !== 'function') {
throw Error('callback is not a function');
}
let acc = initial;
for(i=0;i<this.length;i++) {
acc = callback(acc, this[i], i, this);
}
return acc;
}
First, we check if the reduce method was called on a null or undefined object. Then we check if the passed callback is a function.
After the initial type checks, we assign the passed initialValue to the accumulator. Then we loop through the array and call the callback for each item in the array. At the end of execution, we have to return the accumulator value.
We are using this implementation only to help you understand how the reduce method actually works. For example, you can see that it uses a for loop to iterate through the array under the hood.
However, note that you shouldn’t use this implementation in your production code. In fact, prototyping methods to Javascript standard types is a bad coding practice you should never indulge in.
Summary
In this post, we discussed one of the most useful Javascript array methods, Javascript reducer. We discussed how and when to use it and even build our own reduce method from scratch.
I hope this knowledge will help you to identify problems that can be resolved with the use of reducer in the future. Some of these use cases overlap with forEach , map
, and filter
array methods. So you should know to pick the situations that can be solved optimally using reduce method.
Source: livecodestream