## DEV Community is a community of 638,459 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

loading... # We Don't Need No Stinking map() or filter()

Pretty clickbait-y title, I know, but hear me out! I posed a shower thinking 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:

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

``````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: 
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?

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

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

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

``````[].pushAndReturn(doubler(1)) = 
// remaining values to accumulate: [2,3,4,5]
.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: 
[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 \$#!+ Image from Outside Magazine

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

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

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

``````isOdd(1) ? [].pushAndReturn(1) : [] = 
// remaining values to accumulate: [2,3,4,5]
isOdd(2) ? .pushAndReturn(2) :  = 
// remaining values to accumulate: [3,4,5]
isOdd(3) ? .pushAndReturn(3) :  = [1,3]
// remaining values to accumulate: [4,5]
isOdd(4) ? [1,3].pushAndReturn(4) : [1,3] = [1,3]
// remaining value to accumulate: 
isOdd(5) ? [1,3].pushAndReturn(5) : [1,3] = [1,3,5]
// remaining values to accumulate: []

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

# So... why? 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!

## Discussion (19) JavaScript Joel

Great article!

And I could show you a real use case for this. I have created MojiScript and implemented map, filter and reduce. Both map and filter internally call reduce. Here's some examples:

``````const map = func => iterable => reduceWhile (null) (asyncMapReducer (func)) ([]) (iterable)
``````

github.com/joelnet/MojiScript/blob...

``````const filter = predicate => iterable => reduceWhile (null) (asyncFilterReducer (predicate)) ([]) (iterable)
``````

github.com/joelnet/MojiScript/blob...

Well technically they (and also `reduce`) call `reduceWhile`. Augie De Blieck Jr.

I learned this one time when digging into the source code for the Elixir language. The Enum module there has map(), filter(), and reduce() -- but both map and filter are defined as reduce functions.

You can see it over here:

github.com/elixir-lang/elixir/blob...

I wouldn't be surprised if every other language does the same, now that I think about it.

So, yeah, write the code that reads the best for the sake of future coders, and the language will take care of simplifying the rest for you. ;-) Brad Gilbert

Perl 6 doesn't do it this way, because it can't do that easily.

If you tried to implement `map` with `reduce` it would hang before producing a single value when called on an infinite sequence.

``````my @a = (0..Inf).map( *² ); # infinite sequence of all squared values

.say() for @a.head(20); # prints first twenty squares
``````
``````# sequence of only twenty squared values
my @b = (0..^20).reduce: { @_.head(*-1).Slip, @_.tail² }

.say() for @b; # prints the same twenty squares
``````
``````# doesn't stop until it is killed or runs out of resources
my @c = (0..Inf).reduce: { @_.head(*-1).Slip, @_.tail² }

.say() for @c.head(20); # never gets to this line of code
``````

You could maybe find a way to pass upwards the number of values to produce:

``````my @d = (0..Inf).reduce: {
last if @_ > 20; # stop early

@_.head(*-1).Slip, @_.tail²
}

.say() for @d;
``````

The thing is that iteration is done using an object based protocol, so `map` and `grep` just return an object with the appropriate functionality.
(Most of the time this is abstracted away so you don't need to know how it works.)

Perl 6 is a functional language while also being a procedural language.
So it can't always make the same sort of guesses about what you mean that pure functional languages can make. Kushan Joshi

While reduce can pretty much do anything, I would recommend that you also mention that not to use the same hammer for every kind of nail. We developers like to stay in our familiar territory and that’s why we avoid trying out new things, it would really suck if a fellow developer uses reduce to do everything, when they could have used other more optimised things. Sergiu Mureşan

I used `filter`, `map` and `reduce` almost every day at work and never thought of that! Great article!

Maybe use `reduce` instead when you both want to filter and map at the same time? Or maybe you want to do some other operations as well. `reduce` is like the swiss army knife of JS. Alain Van Hout

Before anyone would be inclinded to use `reduce` for everything, since it indeed can be used to do all the things that `map`and `filter`(as well as `some`and `any`), keep in mind that the same could be said (and then some) about a traditional `for` loop.

As to using reduce when you want to combine different operations, again note that the same would be true for a `for`loop. Neither a `for` loop nor `reduce`should be used in those cases, for the very same reason: you sacrifice legilibity (and the next developer that has to modify your code won't be thanking you for your cleverness). IsaacLeimgruber

I remember doing the implementation of map and fikter if I remember correctly, it was in scala. Whatever, map and filter are good because they are cornerstones of the bridge between high order abstraction and intuition Jason Steinhauser

In JavaScript, there would be a slight performance hit if you used `reduce` to perform a `map` operation because of returning an array each time. However, immutable data structures in other languages (like Clojure's `vector`) would not really see a performance hit because of how they are stored. You've given me an idea on a follow up post to discuss immutability and lazy vs. eager evaluation. Thanks for the feedback!