Deconstructing Map, Filter, and Reduce
Tags: javascript, functional, reduce
Published: 2018-11-27 07:57:34

---
title: Deconstructing Map, Filter, and Reduce
published: true
description: Master `map`, `filter`, and `reduce` by deconstructing and rebuilding them from scratch.
tags: JavaScript, functional, reduce
---

[![Disassembled Camera](https://thepracticaldev.s3.amazonaws.com/i/e6wgpdwfjtbisxumi7k4.jpg)](https://pixabay.com/en/digital-camera-disassembly-336670/)

Today we will master `map`, `filter`, and `reduce` by deconstructing and rebuilding them from scratch.

When I was little, I received a watch as a gift. Much to my mother's horror, the first thing I did was grab the smallest screwdriver I could find and take it apart piece by piece. I wanted to see the internals and inspect every part.

To my mother's relief, I was able to put the watch back into it's original working state. Having thoroughly inspected the internals, I walked away with a better understanding of what makes a watch tick.

![Pulp Fiction watch scene with Captain Koons](https://thepracticaldev.s3.amazonaws.com/i/u3oy3w6o82oif1121nkm.jpg)

Today, I still like to take things apart to better understand them. It's a practice I would also encourage you to do.

Let's start by looking at `reduce` externally. Right away I can identify 4 parts. The `array`, the `method`, the `reducer` and an `initialValue`.

```javascript
const items = [ 1, 2, 3, 4 ]
const initialValue = 0
const reducer = (accumulator, currentValue) => accumulator + currentValue
items.reduce(reducer, initialValue) //=> 10
/* \     \      \          \
  array   \      \           - initial value
        method    \
                reducer
*/
```

Everything is pretty self explanatory. Everything except for the `reducer`. This requires a further breakdown.

Note: Reducers have 4 parameters, for now we will ignore the last 2 and focus on the `accumulator` and `currentValue`.

These parameters are typically abbreviated as `acc` and `cur`.

```javascript
const reducer = (acc, cur) => acc + cur
```

Because you are already familiar with for loops, I can use the for loop below to help demonstrate what the `accumulator` and `currentValue` are and how they are used.

```javascript
const items = [ 1, 2, 3, 4 ]
let acc = 0
//         \
//       initial value
for (let i = 0; i < items.length; i++) {
  const cur = items[i]
//        \
//     current value
  acc = acc + cur
//     \
//   update the accumulator
}
```

And to insert the `reducer`...

```javascript
for (let i = 0; i < items.length; i++) {
  const cur = items[i]
  acc = reducer(acc, cur)
}
```

If you want to see more breakdowns like this, check out [Map, Filter, Reduce vs For Loops (syntax)](https://dev.to/joelnet/map-filter-reduce-vs-for-loops-syntax-2k5l).

# The Accumulator

In the example above, the `accumulator` is a `Number`, but it doesn't have to be a `Number`, it can be any type.

In this example, `acc` is an `Array` and the `reducer` pushes a doubled value into the `accumulator`.

```javascript
const items = [ 1, 2, 3, 4 ]

const reducer = (acc, cur) => {
  acc.push(cur * 2)
  return acc
/*         \
   The reducer must always return the accumulator
*/       
}

let acc = []

for (let i = 0; i < items.length; i++) {
  const cur = items[i]
  acc = reducer(acc, cur)
}

acc //=> [ 2, 4, 6, 8 ]
```

In this example, the `accumulator` is an object and new values are added to the object.

```javascript
const items = [ 1, 2, 3, 4 ]

const reducer = (acc, cur) => {
  acc[cur] = cur * 2
  return acc
}

let acc = {}

for (let i = 0; i < items.length; i++) {
  const cur = items[i]
  acc = reducer(acc, cur)
}

acc //=> { 1:2, 2:4, 3:6, 4:8 }
```

You should notice between these examples, the for loop code was identical. Don't believe me? Go ahead scroll back and check! Only the `initialValue` and the `reducer` changed. So whether the `accumulator` is a `Number`, an `Array`, an `Object`, or some other type... You only need to change the `initialValue` and the `reducer`, not the loop!

# Reduce

Because we know the for loop never changes, it is easy extract it into it's own function, `reduce`.

```javascript
const reduce = () => {
  for (let i = 0; i < items.length; i++) {
    const cur = items[i]
    acc = reducer(acc, cur)
  }
}
```

Your linter should be complaining about missing `reducer` and `items` so let's add those. We'll also add an `initialValue` while we are at it.

```javascript
const reduce = (items, reducer, initialValue) => {
  let acc = initialValue
  for (let i = 0; i < items.length; i++) {
    const cur = items[i]
    acc = reducer(acc, cur)
  }
  return acc
}
```

Is that it? Did we just create `reduce`? Seems too simple!

Well, we did ignore those 2 extra parameters in the `reducer`. Also, the `initialValue` in `reduce` should be optional, but it's required in our version. We'll get to that later.

# Map

It could be said that `map` is a derivative of `reduce`. In that case, we can use our `reducer` from above, pass this into `reduce` and supply an initial value of `[]`. The initial value is `[]` because our result will be an `Array`.

```javascript
const map = (items, func) => {
//                    |
//        function to modify value
  const initialValue = []
  const reducer = (acc, cur) => {
    acc.push(func(cur))
//            |
//      execute func on the currentValue
    return acc
  }
  return reduce(items, reducer, initialValue)
}

const double = x => x * 2

map(items, double) //=> [ 2, 4, 6, 8 ]
```

# Filter

`filter` is almost exactly the same as `map`. We just have to change the `reducer` to filter values based on the results from the `predicate`.

```javascript
const filter = (items, predicate) => {
//                         |
//       if truthy, append to accumulator
  const initialValue = []
  const reducer = (acc, cur) => {
    if (predicate(cur)) {
//         |
// run predicate on currentValue
      acc.push(cur)
    }
    return acc
  }
  return reduce(items, reducer, initialValue)
}

const isEven = x => x % 2 === 0

filter(items, isEven) //=> [ 2, 4 ]
```

# Other Features

The `initialValue` in `reduce` should be optional. We should be able to do this and get a result of `10`, instead we get `NaN`.

```javascript
const add = (acc, cur) => acc + cur

const items = [ 1, 2, 3, 4 ]

reduce(items, add) //=> NaN
```

How would you make `initialValue` optional? Show off your code in the comments.

I mentioned above that a reducer takes 4 arguments. All 4 arguments are:
- Accumulator (accumulator)
- Current Value (currrentValue)
- Current Index (currentIndex)
- Source Array (source)

We have already implemented the `accumulator` and `currentValue`. How would you implement `currentIndex` and `source`? Show me your code in the comments.

# Extra Credit

Modify `reduce` to work with both an `Array` and an `Iterator`. This is something `Array`'s reduce cannot do.

```javascript
// range is an Iterator.
const range = require('mojiscript/list/range')

const reduce = (items, reducer, initialValue) => {
  let acc = initialValue
  for (let i = 0; i < items.length; i++) {
    const cur = items[i]
    acc = reducer(acc, cur)
  }
  return acc
}

const add = (acc, cur) => acc + cur

// Make this return 10
reduce(range(0)(5), add, 0)
```

Create a `reduceWhile` function. This is just like `reduce`, but takes an extra function that will break the iteration when a given condition is met. Think of this as the `break` in a for loop.

```javascript
const predicate = (acc, cur) => acc + cur < 7

const reduce = (items, predicate, reducer, initialValue) => {
  /* solution goes here */
}
```

# P.S.

This article ordered the arguments in a specific way to be easier to read for beginners. But if I were to design these functions to be FP friendly, I would order the arguments as such:

- predicate
- reducer
- initialValue
- list

# Summary

After deconstructing `map`, `filter`, and `reduce` to learn their inner secrets they become so much more accessible to us.

It's easy to see that by building your own `reduce`, you can expand on the features like being able to support an `Iterator` or break early. I have gone even further with [MojiScript's `reduce`](https://mojiscript.js.org) by supporting an `async Iterator` as well as an `async reducer`.

Was there something you'd like me to go into more detail? Did you learn something by reading this article? Let me know in the comments!

If you love Functional JavaScript, follow me here or on Twitter [@joelnet](https://twitter.com/joelnet)!

![Cheers!](https://thepracticaldev.s3.amazonaws.com/i/6jsy3a866frzp3u5oda0.jpg)