Map, Filter, Reduce vs For Loops (syntax)

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 ]

Functional

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

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

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 ]

Functional

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

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

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

Functional

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

output //=> 6

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 ]

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 ]

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 ]

Source: dev