The Array.reduce method in JavaScript is an incredibly versatile and powerful array iterator. I had some initial trouble wrapping my brain around it, but now I use it frequently! Hopefully this guide will help others who might be having trouble.
Example Data
Before we get started, let's create some simple data that we will use in the code examples below. Here are two simple arrays of food items, one for lunch and one for dinner. (I like using food for examples, in case you hadn't noticed yet. 🍕🌮🍰)
Here is the lunch
array:
const lunch = [
{
name: "Egg Salad Sandwich",
quantity: 1,
caloriesPer: 450,
price: 4.99
},
{
name: "Chips",
quantity: 1,
caloriesPer: 190,
price: 1.79
},
{
name: "Bottled Water",
quantity: 1,
caloriesPer: 0,
price: 1.49
}
]
And for the dinner
array:
const dinner = [
{
name: "Tacos",
quantity: 2,
caloriesPer: 340,
price: 6.00
},
{
name: "Refried Beans",
quantity: 1,
caloriesPer: 210,
price: 2.69
},
{
name: "Soda",
quantity: 1,
caloriesPer: 190,
price: 1.99
}
]
What is the Reduce Method?
The Array.reduce
method iterates through each element in an array. Upon each element, it performs a reducer
function that returns some output to an accumulator
variable.
This accumulator
variable is used as a running total after each iteration of the reducer
function. Once all elements in the array have been processed, the accumulator's final contents are returned!
Here's the documented format:
arr.reduce(
callback(accumulator, currentValue[, index[, array]])
[, initialValue]
)
I know that sounds and looks intimidating, but I promise it's not! We'll go over some examples below that should clear that up.
How to Use Reduce
1. Basic usage scenario
Let's say we need to figure out the total calories of our lunch. We could use forEach
, or map
here... but this is a perfect job for reduce:
const totCalories =
lunch.reduce((total, el) => total + el.caloriesPer * el.quantity, 0);
Or, you could do the same thing in a more verbose form:
const totCalories = lunch.reduce((total, el) => {
return total + el.caloriesPer * el.quantity;
}, 0);
You can also use a separate reducer function, like this:
const caloriesReducer = (acc, el) => {
return acc + el.caloriesPer * el.quantity;
}
const totCalories = lunch.reduce(caloriesReducer, 0);
After executing any of the above, our totCalories
variable should contain the numeric value 640
.
2. A bit more complex usage
Many of the examples I've seen online stop at what we've done above. You are NOT limited to just calculating a single value and returning it!
Let's demo that by totaling both calories and price, and returning an object with both items:
const totals = lunch.reduce((total, el) => {
const calsTemp = total.calories + el.caloriesPer * el.quantity;
const costTemp = total.cost + el.price;
return { calories: calsTemp, cost: costTemp }
}, { caloriesTotal: 0, costTotal: 0 });
Note how we are setting initial values for both the calories
and cost
key/value pairs. We'll discuss that more later.
After executing that, the totals
variable would contain this object:
{
"caloriesTotal": 640,
"costTotal": 8.27
}
3. Reduce can do a LOT More
The reduce
method, despite its name, can do much more than just reducing data down to singular values. You can actually use reduce to perform many of the other common array methods like concat
, map, filter, and more.
I don't recommend doing this normally, but I think it's a great learning tool to demonstrate how reduce
works!
Using reduce to concatenate arrays
What if we just wanted to combine our lunch and dinner arrays? Obviously, the concat
method is definitely the easiest way:
const allItems = dinner.concat(lunch);
But just to demonstrate one way we could do the same with reduce
:
const allItems = lunch.reduce((total, el) => {
total.push(el);
return total;
}, dinner);
The "trick" here is that we set our initial accumulator value to be the dinner
array. Then we just push
each element onto it... a.k.a. accumulating items! You can do this with anything: strings, objects, even promises!
Is this making more sense yet? I bet few of you just had a lightbulb moment! 😮💡
Using reduce to mimic filter and map
As a final example to drive it all home, let's use reduce
to mimic the combination use of the filter
and map
methods.
Let's filter out the items in our lunch
array that are below 300 calories, and then convert the name
values to all lowercase characters.
Using filter
and map
, it would look like this:
const lunchEdited = lunch
.filter(item => item.caloriesPer < 300)
.map(el => ({...el, name: el.name.toLowerCase()}));
And using reduce
, we could do it like this:
const lunchEdited = lunch.reduce((total, el) => {
if (el.caloriesPer >= 300) return total; // skip it!
return total.concat({ ...el, name: el.name.toLowerCase() });
}, []);
After executing either of the above, the lunchEdited
variable would contain:
{
"name": "chips",
"quantity": 1,
"caloriesPer": 190,
"price": 1.79
},
{
"name": "bottled water",
"quantity": 1,
"caloriesPer": 0,
"price": 1.49
}
Notice how similar they are? I could be totally mistaken, but I almost feel like the map
and filter
methods are just a shorthand form of reduce
.
Another JavaScript "Gotcha!"
Even though the official docs say it's not required, you should always set an initial value for the reducer! (You'll notice I've done that with all of the examples on this page.)
There are two reasons for this:
If an initial value isn't set, the iterator function will start with element index
1
, skipping the first one. Strange output values and/or TypeErrors are likely... and you'll spend hours figuring out why.If you're using
reduce
with an array of objects (like we are), an initial value is simply required.
TLDR: Just do yourself a favor by setting an intial value every time you use reduce.
When Should You Use Reduce?
That's a good question... but the answer is entirely up to you!
I will typically use map/filter when I am working with an array and want to return an array. But if I want something else returned, like calculated totals/averages or an object of many aggregated values... reduce is without question my best friend!
You'll figure out what works best for you. It's an easier decision to make once you're familiar with the available tools. (And that's the entire reason I'm writing these posts!)
Final Thought
That's all for this post! I really hope it helps a few people have that "ah haaa! 💡" moment I first had with reduce
.
Happy coding! 😎