DEV Community

JavaScript Joel
JavaScript Joel

Posted on • Edited on

Map, Filter, Reduce vs For Loops (syntax)

The intention of this post is not to say one paradigm is better than the other. It is just to show common imperative patterns you run into and their functional equivalents.

Sometimes things are easier to learn if you can relate it to something you are already familiar with and be given a map on how to transition back and forth.

Previously, I had done something similar at Functional vs Imperative Patterns in JavaScript

Map

map takes a list and runs a function on each item in the list, returning the same length list.

Imperative



const double = x => x * 2
const input = [ 1, 2, 3 ]
const output = []
for (let i = 0; i < input.length; i++) {
  output.push(double(input[i]))
}

output //=> [ 2, 4, 6 ]


Enter fullscreen mode Exit fullscreen mode

Functional



const double = x => x * 2
const input = [ 1, 2, 3 ]
const output = input.map(double)

output //=> [ 2, 4, 6 ]


Enter fullscreen mode Exit fullscreen mode

Filter

filter takes a list and returns a list containing all items that match the predicate. In this example isEven is the predicate.

Imperative



const isEven = x => x % 2 === 0
const input = [ 1, 2, 3, 4, 5 ]
const output = []
for (let i = 0; i < input.length; i++) {
  if (isEven(input[i])) {
    output.push(input[i])
  }
}

output //=> [ 2, 4, 6 ]


Enter fullscreen mode Exit fullscreen mode

Functional



const isEven = x => x % 2 === 0
const input = [ 1, 2, 3, 4, 5 ]
const output = input.filter(isEven)

output //=> [ 2, 4, 6 ]


Enter fullscreen mode Exit fullscreen mode

Reduce

reduce takes a list and returns any data structure. It could be another list or an object.

Simple

Imperative



const add = (x, y) => x + y
const input = [ 1, 2, 3 ]
const initial = 0
let output = initial
for (let i = 0; i < input.length; i++) {
  output = add(output, input[i])
}

output //=> 6


Enter fullscreen mode Exit fullscreen mode

Functional



const add = (x, y) => x + y
const input = [ 1, 2, 3 ]
const initial = 0
const output = input.reduce(add, initial)

output //=> 6


Enter fullscreen mode Exit fullscreen mode

Complex

Imperative



const isEven = x => x % 2 === 0
const double = x => x * 2
const input = [ 1, 2, 3, 4, 5 ]
const initial = []

let output = initial
for (let i = 0; i < input.length; i++) {
  if (isEven(input[i])) {
    output.push(double(input[i]))
  }
}

output //=> [ 4, 8 ]


Enter fullscreen mode Exit fullscreen mode

Functional

It could be written like (below) but know that it will iterate over the Array twice.



const isEven = x => x % 2 === 0
const double = x => x * 2
const input = [ 1, 2, 3, 4, 5 ]
const initial = []

let output =
  input
    .filter(isEven)
    .map(double)

output //=> [ 4, 8 ]


Enter fullscreen mode Exit fullscreen mode

Alternatively, you can create a reducer that can both filter and map in a single iteration.



const isEven = x => x % 2 === 0
const double = x => x * 2
const input = [ 1, 2, 3, 4, 5 ]
const initial = []

const reducer = (filter, map) => (acc, x) => {
if (filter(x)) {
acc.push(map(x))
}
return acc
}

const output = input.reduce(reducer(isEven, double), initial)

output //=> [ 4, 8 ]

Enter fullscreen mode Exit fullscreen mode




End

I'm currently available for part time contract work (C#, JavaScript, React). Hit me up on Twitter or linkedin to get ahold of me.

My articles are very Functional JavaScript heavy, if you need more FP, follow me here, or on Twitter @joelnet!

More articles
Deconstructing Map, Filter, and Reduce
Ask me dumb questions about functional programming
Let's make a DEV.to CLI... together
Let's talk about auto-generated documentation tools for JavaScript

Cheers!

Top comments (12)

Collapse
 
mesteche profile image
Mesteche

Nice comparison, also the last functional example can avoid the if statement :

const isEven = x => x % 2 === 0
const double = x => x * 2
const input = [ 1, 2, 3, 4, 5 ]
const initial = []

const reducer = (filter, map) => (acc, x) =>
  filter(x) ? acc.concat(map(x)) : acc

const output = input.reduce(reducer(isEven, double), initial)

output //=> [ 4, 8 ]
Collapse
 
joelnet profile image
JavaScript Joel

I was trying to keep it simple for noobs but I think your solution is much cleaner.

Typically I will use a mutable accumulator in a reducer. Your version is immutable so every iteration will produce a new array. Not the best for performance but this is just for example anyway.

Collapse
 
mesteche profile image
Mesteche

Then with a mutable accumulator :

const reducer = (filter, map) => (acc, x) =>
  (filter(x) && acc.push(map(x)), acc)
Thread Thread
 
joelnet profile image
JavaScript Joel • Edited

Awesome!

I'm a big fan of the comma operator. I decided to leave it out in this code block to not confuse anyone. But I use it pretty regularly in my own codebase.

Collapse
 
johnboy5358 profile image
John

Hi Joel,

I'm one of those noobs, but trying my very best not to be. I am trying to teach myself functional programming in JavaScript and I have come upon the subject of transducers. This code looks very similar; can you confirm that this code is, or is closely related to the concept of transducers.

Thanks

Thread Thread
 
joelnet profile image
JavaScript Joel

Transducers are the next evolution. You would start with a list, and learn map, filter, reduce. Once you start applying multiple map/filter/reduces to a single list, you realize you are enumerating the list multiple times, which will slow down your application.

Transducers are a way to apply those multiple map/filter/reduces while enumerating the list one time.

Collapse
 
callmeareks profile image
Alejandro González

Ternary operator it's still an if statement in disguise.

Collapse
 
mesteche profile image
Mesteche • Edited

Not exactly, an if statement doesn't evaluate to a value, in other words, it doesn't return anything.
The ternary evaluates to something.
medium.com/javascript-scene/nested...

Collapse
 
dimpiax profile image
Dmytro Pylypenko

It's all functional.
Comparison between Imperative vs Declarative.

Collapse
 
ondrejs profile image
Ondrej

I still fight with this (though in Kotlin, not JavaScript). OOP/procedural style is much convenient, but I do understand importance of declarative coding because it's much more elegant and pure.

Collapse
 
joelnet profile image
JavaScript Joel

I'd like to argue that imperative code isn't any more convenient, but that it is more familiar.

If you had learned a declarative style first you would feel the same way about oop.

Collapse
 
ondrejs profile image
Ondrej

Hmmm...probably correct. I have not thought about it in this way.