clubmate.fi

A good[ish] website

Web development blog, loads of UI and JavaScript topics

Reduce: the multitool of JavaScript array manipulation

Filed under: JavaScript— Tagged with: iteration

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 few.

But there’s a sane limit of how many custom methods you can have in a language. Thank god 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.

Higher order function is a function that takes a function as a parameters.

Reduce syntax

A good way to look at reduce is to compare it to a map, it has two differences:

var foo = ['foo', 'bar'].map((currentValue, index, array) => {
  return // Do something...
})

var bar = ['foo', 'bar'].reduce((previousValue, currentValue, index, array) => {
  return // Do something...
}, {})
  1. The callback has one extra argument: previousValue, also referred to as accumulator.
  2. The method has a second parameter: initialValue, which is not always needed, but it can be for example an empty object {} or 0.

And this gives it two features map doesn’t have:

  1. 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.
  2. Possibility to return other data type than array, like object, or anything really, int, string...

Reduce arguments

callback
A function to execute on each element in the array, except for the first, if no initialValue is supplied.
previousValue
The value the callback function produced on its previous iteration. This concept is in the heart of reduce.
currentValue
This is the value of the current iteration, exactly same as in map.
currentIndex
The index. Just as in map.
array
The original array. Just as in map.
initialValue
A value to use as the first argument to the first call of the callback. If no initialValue is supplied, the first element in the array will be used as the initial accumulator value and skipped as currentValue.

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 🔥

Get the 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.48

This is a great example, because it uses all four of the callback’s arguments.

Returning an object

This is kind of cool, literally 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 the accumulator on the first run.
  • The reduce method’s return value should be an object.
  • The accumulator is spread at the end.
const countries = [
  ['ch', 'Switzerland'],
  ['de', 'Germany'],
  ['fr', 'France']
]

const regions = countries.reduce((accumulator, [countryCode, name]) => {
  return { [countryCode]: name, ...accumulator }
}, {})

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 to be had if 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.

Comments would go here, but the commenting system isn’t ready yet, sorry.

  • © 2022 Antti Hiljá
  • About
  • All rights reserved yadda yadda.
  • I can put just about anything here, no one reads the footer anyways.
  • I love u!