A beginner-friendly guide into the JavaScript’s reduce method. Spiced up with analogies and examples.
Why reduce might be needed?
JavaScript provides some handy Array methods, like, map, filter, includes,
some, and every, just to mention a few.
This guide to common array methods
has examples of several of them working with object keys.
But there’s a sane limit of how many custom methods you can have in a language. Thank goodness JavaScript is not like PHP, where you can just type a sentence and slap parens at the end and et voilà, it probably does what you want:
// Example of a typical native PHP function
plz_make_that_thing_like_backwards_and_then_tally_them_up_after()Rather than being a collection of functions, JavaScript is a programming languages which provides versatile methods like reduce. Reduce is the Swiss army knife of array methods, a higher order function that lets you build your own tools.
Reduce syntax
Perhaps a helpful way to look at reduce is to compare it to a map, it has
two differences:
const foo = ['foo', 'bar'].map((currentValue, index, array) => {
return // ...
})
const bar = ['foo', 'bar'].reduce((prevValue, currentValue, index, array) => {
return // ...
}, {})- The callback has one extra argument:
prevValue, also referred to asaccumulator. - The method has a second parameter:
initialValue, which is not always needed, but it can be for example an empty object{}or0.
And this gives it two features map doesn’t have:
- Access to the previously processed values during the iteration, or another way of phrasing: reduce is able to keep track of the accumulated state internally.
- Possibility to return other data type than array, like object, or anything really, int, string...
Reduce arguments
callbackA function to execute on each element in the array, except for the first, if no initialValue is supplied.
previousValueThe value the
callbackfunction produced on its previous iteration. This concept is in the heart of reduce.currentValueThis is the value of the current iteration, exactly same as in
map.currentIndexThe index. Just as in
map.arrayThe original array. Just as in
map.
initialValueA value to use as the first argument to the first call of the
callback. If noinitialValueis supplied, the first element in the array will be used as the initial accumulator value and skipped ascurrentValue.
Reduce examples
Below are some examples to illustrate how it actually works.
Summing up numbers in an array
The most basic example is to sum up all the values in an array:
const coolNumbers = [69, 420, 666]
const holyNumber = coolNumbers.reduce((total, amount) => {
return total + amount
})
console.log(holyNumber) // 1155 🔥Calculate an average
The formula for an average is: sum of all the values /
total amount of items.
Here’s the logic in English:
- Keep summing up the values
total += amount. - When hit the last item: divide the total with the item count.
- Return
total.
const moneys = [76.21, 420.69, 86.54]
const average = moneys.reduce((total, amount, index, array) => {
total += amount
const isLast = index === array.length - 1
if (isLast) return total / array.length
return total
})
console.log(average) // 194.48This is a great example, because it uses all four of the callback’s arguments.
Returning an object
This is useful, and it is the only array method that can do this in JavaScript.
Here we need to use the second param of the reduce method, the initialValue.
Maybe we have a nested array structure of ISO-2 country codes and the country names, and we’d like to turn it into an object where the country code is the key, and country’s name the value.
- Pass in an empty object
{}as the second parameter, this will be the value of theaccumulatoron the first run. - The reduce method’s return value should be an object.
- The
accumulatoris spread at the end.
const countries = [
['ch', 'Switzerland'],
['de', 'Germany'],
['fr', 'France'],
]
const regions = countries.reduce((acc, [countryCode, name]) => {
return { [countryCode]: name, ...acc }
}, {})
console.log(regions)
// { fr: "France", de: "Germany", ch: "Switzerland" }Find and group same values in an array
With reduce it’s handy to find all occurrences of the same number from an
array:
const numbers = [1, 1, 2, 3, 4, 4, 4, 5, 5, 5, 5]
const occurrences = numbers.reduce((accumulator, randomNumber) => {
accumulator[randomNumber] = ++accumulator[randomNumber] || 1
return accumulator
}, {})
console.log(occurrences)
// { '1': 2, '2': 1: '3': 1, '4': 3, '5': 4 }This can be handy for example when you’re trying to visualize some data, or figure out if a set of random numbers actually is random.
Conclusions
There’s a whole debate about whether we should use reduce at all. Some people
think it’s too nebulous, because everything that reduce can do can be done with
for loops.
I’ve embedded here a really nice episode of the HTTP 203 podcast with the topic of "Is reduce bad?" It’s worth the watch, there’s some nice nicknacks there.
They reason that junior devs don’t use reduce because it’s complex, and properly marinated seniors don’t use reduce because the pendulum has already swung into its other apex.
Also, there’s a certain (false) purity with reduce and array methods that is
somehow appealing, by having one long, unbreakable chain where you process
things. When using for loops you need set the initial value into a separate
variable at first, which is totally fine, but there’s a weird... what do you
call it? kind of destructive, OCD driven, need for things to be concise and
neatly packaged.
Hope this was helpful. Thanks for reading.