Functional Basics #3: Reduce
Tags: beginners, functional
Published: 2019-10-30 18:02:28

Welcome back! In the third part of this mini-series on functional programming basics, I'll be taking a more in-depth look at a slightly more complex array function -`reduce`.

> If you haven't already read it, I recommend starting with the first article in this series, [Functional Basics #1: Map](https://dev.to/kvsm/functional-basics-1-map-2d0a). I'll be building on some ideas as the series progresses.

### What is it?

Let's recap on the array functions we've looked at so far:

- `map` runs a function on each element of an array, and returns an array of the results.
- `filter` checks each element of an array by running a _predicate function_ on it, and returns an array with only the elements for which the predicate returns `true`.

So what's `reduce`? Well, like the name suggests:

- `reduce` runs a function on each element of an array, and _reduces_ the array to a single value.

Cue a simple example - we have an array of numbers, and we want to add the square of each number to get a total. Before we look at how `reduce` could do this, let's just implement it ourselves, in imperative style, with a `for` loop:

```js
const myArray = [2, 9, 34, 22]
let total = 0

for (let i = 0; i < myArray.length; i++) {
  const num = myArray[i]
  const square = num * num
  total += square
}

// total: 1725

```

If you've been following this series, you know the drill here - let's refactor the calculation out of the for loop:

```js
const myArray = [2, 9, 34, 22]
let total = 0

const addSquare = (total, num) => total + num * num

for (let i = 0; i < myArray.length; i++) {
  const num = myArray[i]
  total = addSquare(total, num)
}

// total: 1725

```

The `addSquare` function which we've extracted here is known as a _reducer function_. It takes the current total and the next number in the array, and returns the new total. This means that if we apply this function to every number in the array, passing in the running total each time, we'll eventually _reduce_ the array down to a single value - the final total.

Here's how that looks using `reduce`:

```js
const myArray = [2, 9, 34, 22]

const addSquare = (total, num) => total + num * num

const total = myArray.reduce(addSquare, 0)

// total: 1725
```

So much cleaner! There's a couple of things to note here:

- The first argument to reduce is our reducer function, `addSquare`.
- The second argument (`0` in this case) is the _initial value_ of `total`. This is equivalent to where we had previously used `let total = 0`.

Getting the right initial value is important! If we miss it out, Javascript will take the first element in the array as the initial value instead of running our reducer on it, and we'll get the wrong result:

```js
const myArray = [2, 9, 34, 22]

const addSquare = (total, num) => total + num * num

const total = myArray.reduce(addSquare)

// total: 1723 - wrong!
```

> A brief interlude for some terminology:
> - The `total` value, the first argument to a reducer function, is more generally known as the _accumulator_ - it 'accumulates' the result of running the reducer on each array element. Commonly shortened to `acc`.
> - `num` here is referred to as the _current element_, shortened to `cur`.

Important to note that a reducer should always return the value which will be the `acc` for the next iteration. Therefore, the definition for ALL reducer functions should look like:

```js
(acc, cur) => {
  // do stuff here
  return nextAccValue
}
``` 

### What else can `reduce` do?

It can build _anything_! You can use it to generate any value - a number, a string, an object, even another array (because an array itself is a value). Here's an example to categorise words into an object by their length:

```js
const words = ['the', 'quick', 'brown', 'fox', 'jumped', 'over', 'the', 'lazy', 'dog']

const result = words.reduce((acc, cur) => {
  // If the key for this word length doesn't exist yet, make it an empty array
  if (!acc[cur.length]) {
    acc[cur.length] = []
  }
  // Add the current word to the right key for its length
  acc[cur.length].push(cur)
  return acc
}, {})

/* result:
{
  '3': [ 'the', 'fox', 'the', 'dog' ],
  '4': [ 'over', 'lazy' ],
  '5': [ 'quick', 'brown' ],
  '6': [ 'jumped' ]
}
*/
```

Note how this time the reducer function is passed directly to `reduce` as an anonymous function - this is common. Also note that the initial value is an empty object, `{}`. The reducer takes this empty object and builds the result by adding keys and values as it goes.

Another handy use of reduce is to avoid having to iterate over an array more times than necessary. Assume we have an array of numbers which we want to:

- multiply by 4
- then filter to only include numbers greater than 20
- then sum the total

We could do it like this:

```js
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]

const times4 = num => num * 4
const greaterThan20 = num => num > 20
const sum = (acc, cur) => acc + cur

const result = nums
  .map(times4)
  .filter(greaterThan20)
  .reduce(sum, 0)

// result: 120
```

This looks nice and clean gives the right result, but has a problem - it loops over the array three times, once each for `map`, `filter` and `reduce`. This could have a noticeable impact on the performance of our code if we're processing very large arrays. Instead, we can refactor the `map`, `filter` and `reduce` into a single reducer which does everything required in one loop of the array:

```js
const nums = [1, 2, 3, 4, 5, 6, 7, 8, 9]

const result = nums.reduce((acc, cur) => {
  // Multiply by 4
  const product = cur * 4
  // If not greater than 20, filter this number out by returning the acc unchanged
  if (!(product > 20)) {
    return acc
  }
  // Otherwise return the sum of the acc and the product
  return acc + product
}, 0)

// result: 120
```

From this we can begin to see how flexible and powerful `reduce` can be. Since we have replaced a `map` and a `filter` operation with a single reducer, it's apparent that we could actually write our own version of the `map` and `filter` functions using only `reduce` (if you're up for the challenge, give it a go and post your solution in the comments!)

Hope this has been helpful, stay tuned for more articles on functional programming soon!

_If you found this article helpful, follow me! I'll be adding more articles soon. Liked it? Like it ❤️! Suggestions/improvements? Comment ⬇️! :)_