In this lesson, you'll familiarize yourself with the array method .reduce().
The .reduce() method can be used to reduce an array into one value. Each element of the array will be iterated over, and then combined in some way as defined by a callback, to produce a single result.
That said, it can also be used to transform arrays into another array or object. So it doesn't necessarily have to reduce the size of the resulting object.
Without the Reduce Method
If you were to do a sum with a .forEach(), or a normal for loop, for instance, you'd have to declare a variable outside its scope:
const values = [1, 2, 3, 4, 5];
let sum = 0; // Variable declared outside callback scope
values.forEach((v) => (sum += v));
console.log(sum); // 15
Here, for every iteration of the .forEach() method, the callback increments the sum variable. This is a common pattern when you want to reduce an array into a single value. However, there is a more "self-contained" way to achieve similar results, which is with the .reduce() method.
With the .reduce() Method
With the .reduce() array method, you can do it without needing to declare a variable outside the scope of the array method:
values.reduce((sum, val) => sum + val, 0); // 15
The .reduce() method is called on the array, and it takes a callback as its first argument. The second argument is the initial value of the accumulator, which is 0 in this case.
The .reduce() method calls the callback ((sum, val) => sum + val) with four arguments (two of which are used above):
- The previous return or the accumulator
- The current element
- The index
- The original array
What does the accumulator mean?
It's whatever value you return from the callback. .reduce() takes the return values from the callback to pass into the callback of the next iteration as the new accumulator.
In this case, the accumulator starts as zero, this is passed into the first iteration as the first argument to the callback. The first iteration of the callback takes the value of the accumulator and adds it to the current element. This is then returned implicitly from the callback and passed into the next iteration as the new accumulator.
Optionally, Start Without an Accumulator
Often, you don't have to specify an initial value for the accumulator. If you don't, the first element of the array will be used as the initial value of the accumulator:
values.reduce((sum, val) => sum + val); // 15
Since there is no accumulator specified, the sum is set to the value of the first item, and the first iteration actually starts at the second element:
const values = [1, 2, 3, 4, 5];
const sum = values.reduce((sum, val) => {
console.log(sum, val);
return sum + val;
});
/* OUTPUT
1 2
3 3
6 4
10 5
*/
Note how the first iteration starts with the second element, 2, and the first element, 1, is used as the initial value of the accumulator.
Create an Object From an Array
In the common scenario of having a bunch of data that is given to you in a two-dimensional array, like a table, and you want to create an object, you can do this quite concisely with .reduce():
const data = [
[1, 'nomad', '1000'],
[2, 'Bill', '24'],
[3, 'Picard', '40']
];
data.reduce((obj, row) => {
obj[row[0]] = {
name: row[1],
age: row[2]
};
return obj;
}, {});
/*
{
'1': { name: 'nomad', age: '1000' },
'2': { name: 'Bill', age: '24' },
'3': { name: 'Picard', age: '40' }
}
*/
The code above:
- Has a
datavariable that consists of an array of 3 sub-arrays that represent an ID, a name, and an age. - The reducer defines a callback and also sets the accumulator to
{}, a blank object. - Every iteration:
- The callback takes a row and uses the first element, the ID, to create a new key in the object, which itself will contain an object.
- This sub-object will have two properties,
name, andage. - The
nameis set to the second element in the row - The
ageis set to the third element in the row
That's how you reduce an array into a single object.
Note that when creating objects in this way, you should be careful to ensure that the keys are unique. If you have duplicate keys, the last one would overwrite the previous ones.
The example given is technically correct does demonstrate a bad practice of mutating the accumulator object. You should rather return a new accumulator value each time to avoid side effects.
A safer option that avoids direct mutation (although less memory efficient) could be:
const data = [
[1, 'nomad', '1000'],
[2, 'Bill', '24'],
[3, 'Picard', '40']
];
const result = data.reduce((obj, row) => {
return {
...obj,
[row[0]]: {
name: row[1],
age: row[2]
}
};
}, {});
console.log(result);
Where the accumulator is a new object that is created each time.
This example makes use of the spread operator to create a new object with the same properties as the previous accumulator, and then adds a new property to it. The spread operator will be covered in a later lesson.
Learn by Doing
Copy the examples from this lesson and get them working in your labs. Make a few variations, too.
If you're doing this course step-by-step, and especially if you're in the mentorship program, please be sure to push your work to GitHub when you're done.
Summary: JS Array Method - Reduce
You've just completed a lesson on JavaScript's .reduce() method and its applications where you've:
- Grasped the functionality of the
.reduce()method to condense an array into a single value and its ability to transform arrays into any data structure. - Understood that
.reduce()enables you do reduce without declaring a variable outside the callback scope, keeping the code more self-contained. - Saw how
.reduce()uses a callback with four arguments—the accumulator, current element, index, and original array. - Found out that the accumulator can be omitted and, if so, the first element in the array is used as the initial value, which means iteration starts with the second element.
- Learned the process of transforming an array into an object using
.reduce(), which can be important when working with tabular data or when you need to index data for faster access. - Noted the importance of avoiding the mutation of the accumulator within the callback for cleaner and more predictable results.