Reduce in 5 Minutes
Tags: typescript, javascript, functional, beginners
Published: 2021-10-11 16:30:52

Here's a quick intro to the `reduce()` method in Javascript/Typescript arrays, which is frequently perplexing when encountered in working code.

The code here is written in Typescript but I've tried to keep it friendly for JS readers, and I'll post a link to the equivalent JS at the end. 

## What's the point of reduce?

`Reduce` allows us to take a container of data (like an array) and fold it into another data structure.

The `reduce()` method involves three parts:
- A **container of values**, like an array, with which we need to update another structure (in sequence)
- A **function** that lets us update a value (typically called the _accumulator_) based on an element from our array 

```javascript
function updater(accumulator:SomeType, nextValueFromArray): SomeType {
    ... // whatever operations we want
    return updatedAccumulator
}
```

Frequently this updater is written inline, directly inside the reduce function.

- The last thing the reducer needs is an **initial value** for our accumulator, for the first iteration of the function.
`Reduce` is smart enough to realize that if we don't provide an initial value, it should use the first element of the array as the initial value.

**NOTE**: Omitting the initial value only works if _the accumulator is the same type as elements_. An example to show this will be provided below.


So, to recap, any reduce operation can be thought of as 

```javascript
someArrayOfValues.reduce(updater, initialValueOfTheAccumulator)
```

## Examples

Let's look at some examples!

First, let's see how we could do string concatenation using reduce. This involves 'folding' an array of strings into a single string.

```javascript
// our array of characters to fold
const boSpelling = ['B', 'o', ' ', 'B', 'u', 'r', 'n', 'h', 'a', 'm']


// our initial value for us to reduce into is an empty string 
const initialName = ''
```

Here you see, we write a function that understands how to add a letter to a value and return a new value. `Reduce` takes that function, and our new value, and will pass each letter of our array to that function, bringing the result forward to serve as the accumulated value for the next iteration.

```javascript
const bosName = boSpelling.reduce((nameSoFar, letter) => {
    const updatedName = nameSoFar + letter
    return updatedName
}, initialName)
```

We could also inline the initial value.

```javascript
const bosName = boSpelling.reduce((nameSoFar, letter) => {
    const updatedName = nameSoFar + letter
    return updatedName
}, '')

console.log(bosName) // "Bo Burnham" 
```

Just to provide context, here is the `for` loop version. This does the same as the code above, but here we update a mutable variable and use a `for` block instead of a function expression.

Some people find this preferable but it does require object mutation, unlike `reduce`. 

```javascript
const concatenate = (lst:string[]) => {
    let name = ""
    for (let letter of lst) {
        name += letter
    }
    return name
}

const bosName = concatenate(boSpelling)

console.log(bosName) \\ "Bo Burnham"
```

Now, let's make a custom **sum** function using `reduce`. Combining with es6 syntax allows for some very concise expressions.

```javascript
const numbers = [ 2, 3, 4, 5, 6, 7, 8, 9, 10]

const sum = (lst:number[]) => 
    lst.reduce((count, nextNum) => count + nextNum, 0)
```

Note that since the accumulator `count`, and the array elements are both numbers, we can omit the initial value and just let `reduce` use the first value as the initial.

In situations where they're not the same data type, doing this would cause an error.

```javascript
const sum = (lst:number[]) => 
    lst.reduce((count, nextNum) => count + nextNum)

console.log(sum(numbers)) // "54"
```

## Advanced Examples

We've hit the end of the main things I wanted to demonstrate with reduce (I told you this would be quick). But we can have a bit more fun and show how powerful and flexible it really is. These next examples are beyond the standard use cases of reduce and if you're still new to the concept, feel free to skip them. 

The `reduce()` method can fold a sequence of values into **any** data structure you'd like, including another sequences.

This makes it more powerful than its sibling methods, `map()` and `filter()`, which can only transform an array into another array. But that doesn't mean it can't do what they do as well.

Here we make `map()` from reduce. For each item in the original array, we apply the function to it and add to an accumulator, a new array.

```javascript
const map = <a, b>(func:(arg:a) => b, lst:a[]) => 
    lst.reduce((acc:b[], item) => [...acc, func(item)], [])
```
and we can use it similarly to the `map()` method we know

```javascript
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

const multiplyByThree = (x:number) => x * 3

const mapResults = map(multiplyByThree, numbers)

console.log(mapResults) \\ "3,6,9,12,15,18,21,24,27,30"

```

The `filter` function is similar. Here, the function we pass in is a **condition**, which is a function that accepts a variable of the same type as the ones in the array and returns a boolean).

If the array item satisfies the condition (returns true), we add it to the new array, otherwise we pass along the new array as is.

```javascript
const filter = <a>(condition:(arg:a) => boolean, lst:a[]) => 
    lst.reduce((newLst:a[], item) =>
        condition(item) ? [...newLst, item] : newLst, [])

// our condition
const isEven = (x:number) => x % 2 === 0 ? true : false

const filterResults = filter(isEven, numbers)

console.log(filterResults) \\ "2,4,6,8,10"
```

### A _brief_ aside on types
Another way we can compare the three methods in in terms of the `types` they accept, and return. In pseudocode, the types of the three functions can be described as

`map : (a -> b), Array a -> Array b`
Given a function that takes an `a` and returns a `b`, and an array of `a`, `map` will return an array of `b`.

`filter : (a -> Bool) -> Array a -> Array a`
Given a function that takes an `a` and returns a `boolean`, and an array of `a`, filter returns an array of `a`

`reduce : (b -> a -> b) -> b -> Array a -> b`
Given a function that takes a `b` and an `a` and returns a `b`, an initial accumulator value `b`, and an array of `a`, `reduce` returns a `b`.

## Final Thoughts

I hope this provided some clarity and demystified one of the more powerful tools in the JS toolbelt.

Let me know if this helped, or what other methods you want five minutes on!

## Resources
- See [here](https://www.typescriptlang.org/play?#code/PTAEEFQZwWwQwDYNAFzlFBTUB7AZqAE6YAmArgMaYBQ1IoA6gBZwqgCWUqT2ADjuwB2bfEVKUadMACVxVUIgQ4A7lzJcUOVHADW2KDhjYKOYXCGZCuAiVZxQACgTs9CwQsKE4ATwCUbklApUAAxHARAlB4YDmEtAyNcKMtQWzRoFEJKFDJiWnpQWXJ5IQA3cNLMDSZiPjhCFChguH8E41M0CytRUsQyKoAaUGdXOHd6r28h5XYo0GUmdgomeexBTFJULTJeNP1DbBxkqwwsihziRyFoTABHfsEqXylggCN-SDwyR5R2U25WMNMI1QOpQbtWPtEr0EP1HBRFJtkgoKBQyDAyAhWDhCP5XuhNv8xqBMAhMEZhKA8IRDLhcsNOGwXvQvj8-u4dntCA44Kj0ZjsYQAFwAZQOABVvLxMEN1gAPFAANT6mBCNJgABlGb4haAxUZJdLQABvYKgc2gAB01rNFuIFw5EKwJHAfIxWM0hGCAF9gsFqXcHigEN5uJxwVyOFxlIRZlh3EJnOtYtxsMRithWed2al2MRziHmWAKP5kViMGHBABzVNiDNWdakLjh4lCWbsRCgGFwvA4ulWXlo92Coa9qzIvB5iuzSysHOiZL+77Z0yW4IACRUmEqhCG6YkUeg8AaJMEODIVZWmjEiE42CigPYBGU2BIHVAvBppXYJGwrcE7adt2MocGwUBMOeESglA948FSU5sKS5KYJSwQLnBEw+Ao1TYG2vxASqa70AAcgA8uKACiAJsFE4amCG8w4jozYEMig78h6fbhsiUBwIkKBSn+XBIRSjSWhA7iYHKfG8GSWzQBByhhtG7BIKArzYMEn44N+v6BBpSjKGu+RgGKQzXvmcC8EMYyhvu8g4NKXi-P8CLuBp3DnpebDoEEwRtOAng+KReDKrCVSWvZmAOJykK7rEgEIGF-QheKPCukOAqes8JlAiCSg4DoChgQcJLSTAslVLlk6EBgQxkiCMHYIpqygCYmKBG+GSxtWbWmAi8Zzv86hCDWUVEWAaXhmUFRVKAADkvYRKN81uB4kzWN1o3NnECjQKNcmnKNxn0OeA5BaGojLPUvJYLV8lLSQ1AmIIFavDgIrSkgo2gAAvKAADa80AELzUM804GDC0LeDoPg2QUPzYQiOCIjTCI3AiMwPNAC6uVnQl+HIMBVJ9mCFlyLhu0tpJFUCVtvXPaY04AUTxF8dg-3zfNuXrpY2DeOeNwgS+8yxlge1Zi57gPmw3y-rVaCCCQXAtdecAkIE9gNXd8n2CTYyBPauTjKA6xKcBE11geaB6NUgJS+yNnK-2ZuYBbKrO4EMxqbw6DCbyKw6ykogE5hobXrLVLLtLQxvD1VY-cixBQJibBjso9SRPEliVNhtaCBz0BaHg9Sk+OcHymwM7OeyxkvW9OBQOziT-e9n2kkmVaRZTDgOIXRhiiE9T1cCd3+L9AB8JrUBafWvXLTqkC3nNmxzQ9lwA1Hld2z3awImxGkIkCv1DekMeEdggK85fQovtVBiAGLESawVTiVdiqtAN2w73NxzAAmP66kPpfS7j3DMfcB6YA3vFYOuI-rT1NHPH+R9nQr2AdA2BoBt7wL3uaY2hBHR7BPhzM+4N5o5QbuETAlolBVgcAAAyOr1F6A0UJDQ5FAH6UVdQAB1BAACIhgABJjR-xXgA70jDb5gAAFLqBolobSul2jCCkigIYPBLjcTgmOYYOBHJdksNw1coAppcDfHNHiRdfLIhML+BQ7086vDICgYI2jsCi1ihLewGI0CvDkr0WMcBAl-hduoP85d1JKAoEVIQGBMAa02vYR2-wpKfiqKYwQVt9R8EwI5OSk4Xa0S4JkvAs4wnqTcaBVIOA5rEHuHmQ4rwABWmBzigH8ZwoY3wRhpkpt-ZmbA2GQkLhLf6TgMBChYVWAGOMJ5IPwXlNerdQCCMEcs-RThgQ7xDgQBAGB-DILnhaaBOD-p4Lnr6OehD3DQLPkMheID-5GAAMzANGYNLADh25gNGlQ5mNC6E4AYcwzIP0vkcOltBH6+iCq8CFAIsREiOZvOkbIs2Kg8pcHgKMNqijaSpxiGkrhPDBn0BGr1KKH5YTCSgAANmgN4MwcoFBIBUFwfRbRjGEFDGwu8ZVMlQGyU0JmzzBDog0vdf6ANQAAKGG8oYAAWIYABWIYDKhgAHYhgAA4hgAE4hgAEYAAMeNxUVmJcAqZmg8DESlSYoUkqYDSvmYsoIc9Dn2sdW6kxECJB9zRJ4FCKAADC55hCyg0X6z1IbiDCEjd8Ng28q5+qGGazFZ4aIsDAkIeQbE3RZT7A4BNYbk3CF8F7Ws4cSRklElweo2B3pzFddKqAAxgj32JIYWYtZL4EXCgEUArTFErJpZE2sNUKwGxwoTK+VsACS7huE5E4dGTxqZvBIzWEcWsvFEhpHsAJaUQw3xJ0WNGSCgQERTuJJYGkhB67DOgOiIBkyfX4D9R2l1TrCAesQV6i0X6HX-qgIGqgwbciJojVGzRbsFRxqA+WpN8GcGIZQHGp5BgyQgrBTa0lsLqWUyRYIMRxKAH93A74DFuVwAABFFTgGIuGiiDHdQ9skpUdweK35dKsiOycCBdakq5eqa2VBxLBAcCEDYyAAz3i0OwKsZ5LilI4AQQWZA6mUhYHnewuGfygCrF4KAvBNpsWcbQ0AABNIWPtkDBHWDuUA+mlN1OLkYWivV+nKVAEu6QGpxLwAs4beCImUiIGIBrUMUltR+XoFu0WfGBMWepLSca0SknLFAuSFMbELrTD-LwWSEc9ExxzNeft4Xmiaz1uMYtnF4r2HNutLC1AiOhYADxwCGK8SeDgvgUGEOycUOBwClZDLqPufsvAwCFC0ID7whgBQuotj1upXjzJnrcg+RCvOYECpMSD0Uy0wbDcRd2x2fBCm2zjC+WAYArpu94RbnqTmnLEA6QG1pLSoaw9dor0cRvS3G5NsrDgZzPcEK93weNrlDA9Y84IaVMyqVE5V-44ZuEwFUvUcS+icsrGhwVjCwOny1N4r8KAk5rFwReiQdspgu13z-PV-tkdK7u3a1MJI2iZgwVan7EV7LTA1mRG1utvlOBriI8Ju6vXBuM+Z4Icb4aeBxKFA4ObfF3vLcMWSMYq2DivY2ws3UcAdufe+4fNbJ2ooOGWXPc7obhAhAx-zF062rcPbyzDs3S2p7Aa+y7lXYOcAa46ToKHT2XsXX8M70PAB+X71oAce8i+mV7j3yTx8mDjEPofzS6gz577PCfllI4WY8+gZ4lINQ0FUXN5IcNsHdL8MrwNvDimUFoSZco-3+oQcHtlAAqUAby29RlIvVgfQ-pWerZQAUnlX9X6-0zWgFT5kOEupS6HMkKg0LshU4ia4P9ULDgO-sC7z3vvsoaPT4V5YU-acL8RbulDqAs+SCP+H1AICq9MCvQkwqFsRmNKRsisaCflUO-nRtQnhqAYwi-lYFSpARmGRmIqgW-ufnRhuI5JgF8EgOVuGDwAgLwFwNpmgcrCYkrEztWMZOaEAA) for the full code in a sandboxed environment, in both TS and JS versions.
- See [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) for some more official docs on the method, it's overloads, etc.
- Note there's a small error in the live example where the `isOdd` function actually checks for evenness, and I am being too lazy to fix it and get a new URL.