# Deconstructing Map, Filter, and Reduce 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. 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`.

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

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

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

``````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).

# 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`.

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

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

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

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

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

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

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

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

``````

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.

``````// 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
``````

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.

``````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` 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! ### Discussion   