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
switch
statement.
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 calledacc
for “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
return
an 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
thisArg
it’sinitialValue
- allowing us to specifiy an optional initial value for the loop to begin (which gets passed in asprev
on the first iteration of the loop, withnext
being the first array value) - IMPORTANT: If no
initialValue
is set, Reduce will use your first array value as theprev
on 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 firstprev
value andnext
is our first array value of1
. If there was no initial value thenprev
would be1
(first array value) andnext
would 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
0
we would logTotal: NaN
because 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
initialValue
when 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
return
inside your callback, or your values will beundefined
and evaluate tofalse
- avoid undetected bugs! - You can access the array you’re looping in the third argument of the callback
- You can change the
this
context via a second argument to.reduce(callback, thisArg)
so that any references tothis
inside your callback point to your object - You can use arrow functions with Every but remember that
this
will be incorrect if you also supply athisArg
due to arrow functions not having athis
context - 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!