Follow along with the Exploring JavaScript Array Methods series!
- Exploring Array ForEach
- Exploring Array Map
- Exploring Array Filter
- Exploring Array Reduce (you’re here)
- Exploring Array Some
- Exploring Array Every
- Exploring Array Find
What is Array Reduce?
Array Reduce is a method that exists on the Array.prototype that was introduced in ECMAScript 5 (ES5) and is supported in all modern browsers.
Array Reduce is the most misunderstood array method and a headache for many developers - if only they’d read this article! Pay close attention to the little details and you’ll succeed with Reduce. The concept of Reduce itself isn’t complex, but the method is just a little different to others that we’re used to.
Think of Array Reduce as: “I want to reduce my array to just a single value”
Array Reduce is commonly used for performing things such as math expressions and equations, for instance calculating the total of a numbers array.
You’ll have likely heard the term “Reducer” before when dealing with things such as Redux or NGRX, a Reducer also is a “Reduce”, returning a single value inside a
switchstatement.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
-
Observables and Async Pipe -
Identity Checking and Performance -
Web Components <ng-template> syntax -
<ng-container> and Observable Composition -
Advanced Rendering Patterns -
Setters and Getters for Styles and Class Bindings
Here’s the syntax for Array Reduce:
const reducedValue = array.reduce((prev, next, index, array) => {...}, initialValue);
Our reducedValue will contain our reduced value, this is typically a number - however you can compose objects and arrays inside your Reduce, that’s more of an advanced use case though.
Array Reduce syntax deconstructed:
- Reduce’s first argument is a callback function that exposes these parameters:
prev(sometimes calledaccfor “accumulator” as the value is dynamic, it accumulates the callback’s value and is returned on the next iteration asprev)next(the current element, sometimes calledvalue)index(the element’s index - not commonly used with Find)array(the array we are looping - rarely used)- Inside the body of the function we need to
returnan expression which is then passed to the next iteration asprev- Reduce essentially remembers the value from each iteration and keeps providing you it until your array completes
- Reduce’s second argument is different to other Array method counterparts we’ve explored so far, instead of
thisArgit’sinitialValue- allowing us to specifiy an optional initial value for the loop to begin (which gets passed in asprevon the first iteration of the loop, withnextbeing the first array value) - IMPORTANT: If no
initialValueis set, Reduce will use your first array value as theprevon the first iteration - technically starting the loop on the second array element
See the ECMAScript Array Reduce specification!
In its simplest form, here is how Reduce behaves:
const reduced = [1, 2, 3, 4, 5].reduce((prev, next) => prev + next, 0);
// 15
console.log(reduced);
Simple enough, right? Even if we don’t “get it” right away, we can add up 1, 2, 3, 4, 5 to reach a comfortable 15 - so we’re half way there to understanding Reduce.
When Reduce begins the
initialValue(here it’s0) becomes the firstprevvalue andnextis our first array value of1. If there was no initial value thenprevwould be1(first array value) andnextwould be2(second array value).
These small differences in how Reduce behaves with and without an initialValue likely also contributes to reasons to not fully understanding Reduce.
Developers, like I did, struggled at first with this whole prev and next thing.
So let’s use a really simple demonstration to make sure we get it:
const reduced = [1, 2, 3, 4, 5].reduce((prev, next) => {
console.log(prev, next);
return prev + next;
}, 0);
Outputs in the console (each iteration):
0 1 // 0 = initial value, 1 = first array item
1 2 // 1 = previous result (0 + 1) 2 = second array item
3 3 // 3 = previous result (1 + 2) 3 = third array item
6 4 // 6 = previous result (3 + 3) 4 = fourth array item
10 5 // 10 = previous result (6 + 4) 5 = fifth array item
This now explains why we get 15 as a return value (10 + 5) - there is no next value in our array so Reduce will exit and return that final value to our reduced variable.
We’ve only considered numbers at this point, however as Reduce returns any value type, it has very flexible use cases! You could return flattened Arrays, concatenated Strings, new or merged Objects - or whatever else you can come up with!
That’s the basics of Reduce, let’s take a look at a more real-world scenario where we’ve tasked with calculating the sum from Objects - there are multiple approaches I’m also going to show you.
Using Array Reduce
Here’s our data structure that we’ll be using Array Find with:
const items = [
{ id: '🍔', name: 'Super Burger', price: 399 },
{ id: '🍟', name: 'Jumbo Fries', price: 199 },
{ id: '🥤', name: 'Big Slurp', price: 299 }
];
Let’s calculate the total price of all price properties.
Reducing an Array of Numbers
A basic Reduce will sum an array of numbers, so let’s add an Array Map before to return us just each price property - giving us an array of numbers to then Reduce:
const reduced = items
.map(item => item.price)
.reduce((prev, next) => prev + next);
// Total: 8.97
console.log(found);
This is a perfectly decent example, and completes the mission we set out to achieve to calculate the total price. However, Reduce offers us a really nice way to work with Objects - which involves the use of the initialValue property…
Reducing an Array of Objects
By supplying an initialValue the prev value becomes 0 to begin the Reduce. This works nicely when adding + to our next.price:
const reduced = items
.reduce((prev, next) => prev + next.price, 0);
// Total: 8.97
console.log(reduced);
If we didn’t supply
0we would logTotal: NaNbecause we’d be attempting to add an Object to a number property!
It also saves us another Array Map, keep the code a little cleaner and more efficient.
Give the live demo a try:
Bonus: Reduce-ing without Reduce
Let’s check out a for…in loop example that mimics the behaviour of Array Reduce:
let reduced;
let prev = 0; // "initialValue"
for (let i = 0; i < items.length; i++) {
const next = items[i];
prev = prev + next.price;
}
reduced = prev;
First we declare reduced and prev to assign an initialValue of 0 just like Reduce. From there, we’ll loop our items and create a next variable. We then re-assign prev each time and add + our next.price to it.
Once the loop as finished, I’ve assigned prev to reduced to act like Reduce’s final return value.
Summary
You’ve now learned how to use Array Reduce to reduce your array to just a single value.
Array Reduce is a nice and compact way that we can declaratively reduce an array and return any kind of value we’d like. More advanced use cases include composing Objects and Arrays inside your Reduce, but we’ll save them for another time.
If you are serious about your JavaScript skills, your next step is to take a look at my JavaScript courses, they will teach you the full language, the DOM, the advanced stuff and much more!
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
-
Observables and Async Pipe -
Identity Checking and Performance -
Web Components <ng-template> syntax -
<ng-container> and Observable Composition -
Advanced Rendering Patterns -
Setters and Getters for Styles and Class Bindings
Further tips and tricks:
- Remember to specify an
initialValuewhen dealing with an Array of Objects - Reducing Arrays of Numbers is nice and clean, try not to overcomplicate your Reduce functions - this is a common pattern I’ve seen
- Don’t forget to
returninside your callback, or your values will beundefinedand evaluate tofalse- avoid undetected bugs! - You can access the array you’re looping in the third argument of the callback
- You can change the
thiscontext via a second argument to.reduce(callback, thisArg)so that any references tothisinside your callback point to your object - You can use arrow functions with Every but remember that
thiswill be incorrect if you also supply athisArgdue to arrow functions not having athiscontext - Using Find will skip empty array slots as if it were a falsy value
- You shouldn’t need to in this day and age of evergreen browsers, but use a polyfill for older browsers if necessary
Thanks for reading, happy Reducing!
Go to the next article in Exploring JavaScript Array Methods - Array Some!