We Don't Need No Stinking map() or filter()
Tags: showdev, programming, learning
Published: 2018-09-22 22:29:33

---
title: We Don't Need No Stinking map() or filter()
published: true
description: Reframing your perspective when tools are limited
tags: #showdev #programming #learning
cover_image: https://thepracticaldev.s3.amazonaws.com/i/mbn6czjxx8m2c67yl3qc.jpg
---

Pretty clickbait-y title, I know, but hear me out! I posed a [shower thinking](https://www.businessinsider.com/why-people-get-their-best-ideas-in-the-shower-2016-1) question to some of our junior developers. If you had to choose one of `map`, `filter`, or `reduce` to use for the rest of your life, which one would you pick? While I use `map` and `filter` quite regularly in programs - pretty much independent of language - and I prefer to use them... they're basically derivatives or optimizations of `reduce`.

# How?!

So, let's look at some examples of `Array.prototype.reduce()` in JavaScript. It takes a `callback` function and an optional `initial value`. The `callback` function takes an `accumulator` and the `currentValue` in the array that we're reducing. Reduce is often used to sum the terms of a collection:

```javascript
const sum = (accumulator, currentValue) => currentValue + accumulator

let val = [1,2,3,4,5].reduce(sum,0)

console.log(val)  // => prints 15 to the console
```

Let's walk through the individual steps of what happens. In the first step, we have an accumulated value of 0 (we pass that as the initial value), and the current value is 1. We repeat this for each element in the array, and we return the final accumulated value.

```javascript
sum(0,1) = 0 + 1 = 1     // remaining values to accumulate: [2,3,4,5]
sum(1,2) = 1 + 2 = 3     // remaining values to accumulate: [3,4,5]
sum(3,3) = 3 + 3 = 6     // remaining values to accumulate: [4,5]
sum(6,4) = 6 + 4 = 10    // remaining value to accumulate: [5]
sum(10, 5) = 10 + 5 = 15 // remaining values to accumulate: []

// return accumulator of 15
```

This is a pretty straightforward use case. Reduce seems to be most often used for sums and products.

# You sure we don't need a map?

<div style="text-align:center;">![Lost in the desert](https://thepracticaldev.s3.amazonaws.com/i/29q3nrdrzw2xlmz5aj9v.jpg)Photo from [Julian Peters Photography](https://julianpetersphotography.de/blog/getting-lost-in-the-desert-my-trip-to-the-sahara/)</div>

`Array.prototype.map()` takes a function to apply to each element of the array, and returns the resulting array. The classic example of `map`ping is doubling integers:

```javascript
const doubler = x => x * 2

let val = [1,2,3,4,5].map(doubler)

console.log(val)    // => prints [2,4,6,8,10] to the console
```

Pretty straightforward, right? So how in the world can we build `map` using `reduce`? Instead of our accumulator being a single value now, our accumulator will be an array. We'll apply the function to each `currentValue` and append that to the end of the accumulator array:

```javascript
const pushAndReturn = (arr, item) => { arr.push(item); return arr }

const map = (f, arr) => arr.reduce( (acc, x) => pushAndReturn(acc, f(x)), [])

// Let's try it!
console.log( map(doubler, [1,2,3,4,5]) )    // => prints [2,4,6,8,10]
```

Excellent! Works exactly as we expected. But what were all the steps taken to get that answer?

```javascript
[].pushAndReturn(doubler(1)) = [2]
// remaining values to accumulate: [2,3,4,5]
[2].pushAndReturn(doubler(2)) = [2,4]
// remaining values to accumulate: [3,4,5]
[2,4].pushAndReturn(doubler(3)) = [2,4,6]
// remaining values to accumulate: [4,5]
[2,4,6].pushAndReturn(doubler(4)) = [2,4,6,8]
// remaining value to accumulate: [5]
[2,4,6,8].pushAndReturn(doubler(5)) = [2,4,6,8,10]
// remaining values to accumulate: []

// return accumulator of [2,4,6,8,10]
```

It's important to keep in mind that the accumulator can be **any type we want**. It doesn't just have to be number for summing a list of numbers. It can be literally anything.

# Let's filter our $#!+

<div style="text-align:center;">![Filtering stream water](https://thepracticaldev.s3.amazonaws.com/i/09dpkjt6pwyzrmh4w6hh.jpg)
Image from [Outside Magazine](https://www.outsideonline.com/2316951/know-when-its-time-new-water-filter)

`Array.prototype.filter()` takes a predicate function and returns an array of values that return a truthy value when the predicate is applied. For example, if we want to get only odd numbers out of an array, we can use:

```javascript
const isOdd = x => x % 2 === 1

let val = [1,2,3,4,5].filter(isOdd)

console.log(val)        // => prints [1,3,5]
```

Let's implement filter with just reduce! It's going to seem awfully similar to our map implementation above.

```javascript
const filter = (pred, arr) => arr.reduce( (acc, x) => pred(x) ? pushAndReturn(acc, x) : acc, [])

// Let's try this one now!
console.log( filter(isOdd, [1,2,3,4,5]) )    // => prints [1,3,5]
```

Again, let's walk through the steps to see what happens:
```javascript
isOdd(1) ? [].pushAndReturn(1) : [] = [1]
// remaining values to accumulate: [2,3,4,5]
isOdd(2) ? [1].pushAndReturn(2) : [1] = [1]
// remaining values to accumulate: [3,4,5]
isOdd(3) ? [1].pushAndReturn(3) : [1] = [1,3]
// remaining values to accumulate: [4,5]
isOdd(4) ? [1,3].pushAndReturn(4) : [1,3] = [1,3]
// remaining value to accumulate: [5]
isOdd(5) ? [1,3].pushAndReturn(5) : [1,3] = [1,3,5]
// remaining values to accumulate: []

// return accumulator of [1,3,5]
```

# So... why?
![Ryan Reynolds' existential question](https://media.giphy.com/media/1M9fmo1WAFVK0/giphy.gif)

While this was mostly a thought exercise (I wouldn't necessarily use `reduce()` to replace `map()` in production code), it's important to remember that `reduce()` can return any sort of value. Sure you can do sums, products, and concatenation with reduce... but you can also do mapping, filtering, taking elements while they match a predicate, build tree structures, create dictionaries/maps from lists/arrays... all by using reduce.

Thanks for reading!