Transform Arrays With .reduce()
And fewer loops
This article was close to vanishing into the ether, as in the middle of writing I received an email link to another very solid article about Reduce. However, if this helps even one person in understanding reduce and in writing better iterators, I’ll call it a success. So, here we go.
Whenever writing code that will be performing iterations over an array and transforming it, I first stop and try to think of the best possible way to eliminate any redundancy.
A younger, less experienced me wouldn’t have thought twice about chaining filter and map together. But now my tool of choice is reduce…
Array.reduce()
Array.reduce is, in my opinion, one of the best iterator functions javascript has to offer for transforming an array.
Array.reduce(callback,[initialValue]);
// callback => (acc, item, [idx], [orgArray]) => {};
Let’s walkthrough it real quick... First off, if you’ve never used it before, its an Array.prototype
method and therefore must be called on an array. The parameters that it requires to do its job are a callback
function and an optional initialValue
. The callback
is called by every single item in the Array
. It takes a maximum of four arguments, two of which are optional (acc, item, [idx], [orgArray])
. The two required arguments are acc
, the one you need to understand the most, and item
.
acc
acts as the accumulator for all of the iterations during the reduce cycle. The accumulator is critical to understanding reduce. It is a core concept in recursive functions. It is a stateful value that persists and is passed along through the duration of the reduce function. Each iteration of the reduce cycle returns a modified accumulator value with updates to the state for that iteration. This aides in building up a single result or accumulated value while iterating over a collection. It receives its results from the callback and more than likely will be one of Object
, Array
, or Number
. It defaults to Array[0]
or initialValue
if one is supplied.
• item
is the target of the current iteration in the Array
• idx
is that item’s position in the Array
• orgArray
is the original Array
Some Gotchas
• Reduce must always return the acc
• If supplied with an empty Array
reduce will return the initialValue
Reduce By Hand
Sometimes I find it helpful to see what an actual method is doing behind the scenes. Here reduce is written as a recursive function.
Hopefully that provides a general understanding of how reduce works. So let’s utilize it to transform some arrays.
How to .filter() & .map() with reduce
Add an if statement.
If you’ve ever had to work with Arrays then you’ve also more then likely had to use .filter()
and its relative .map()
.
Here we filter
the records
to ones longer than 60 minutes. Then we map
over them, creating an extra display
property. This works well and is easy to read. However we can do this exact same logic in a single .reduce()
call.
First, we check if the record meets our length criteria of 60 minutes. If it doesn't we return the acc
as is. This acts as our filter
. If it does meet the criteria, then we build up our new acc
.
We begin by destructuring the existing acc
into a new empty array, then we destructure the record into an empty object, and finally we append the display
prop to the new record object. This gives us the exact same result as the .filter().map()
code but with one fewer loop. If you needed to do multiple filters during the iterations of the reduce cycle, you would just need to add more if statements.
How to convert an Array to an Object
Create a unique identifier from item and add it to an Object
Another thing you’ve probably come across is attempting to convert an array to an object. Sure, you can use Object.assign
or es6 spread operator …
But what if you needed more control over the naming of the object keys? Maybe for fast lookups with a unique identifier? You can use reduce
for that as well.
Here, we reduce
over the array of records, setting the initialValue
to an object. As we iterate over the records, we create a unique key inside of the acc
from the title and artist while also setting its value to the record. The acc
will return an Object
whose keys can easily be used to return specific records.
How to remove multiple items from an array
Use reduceRight
A time will come when you’ll want to remove multiple items from an array, at specific indexes and in a single loop, while avoiding items jumping to other indexes. This is where .reduceRight()
comes into play. It does exactly the same thing as .reduce()
but it acts on the original array from right to left.
How to return a different value than initialValue
Check if on the last index and transform acc
If you ever want to return a different value instead of the initialValue
, all you need to do is determine if you’re on the last item
of the orgArray
and return what value you want.
Here we have an apiResponse
and a list of constants that we want to combine for the UI to display. We start by reduce
ing over the list of constants, then we use Array.prototype.find()
to check if the constant was returned by the API. If it was, we append it to the acc
object with the constant as its key. If it wasn't, we still append it but we create a default value for it. Every loop will check if idx === (orgArray.length — 1)
this will tell us if we have iterated over every item. If we have, we return Object.values(acc)
which will result in an array of the new items. Otherwise we return the acc
and go onto the next iteration. We started with two arrays
, reduced it down to an object
with unique keys and default values, then returned an array
of the values when on the last item.
Fin…
I hope these examples show you how helpful reduce
can be and will encourage you to start utilizing it. If you use it in any other way, I’d love to hear about it.