ðŸŠī\zansh.in\js

Play with the controls to see how the code works. All code is present in the page, but some scrollback may be necessary to grok everything.

# reducers / transducers / compose

2 min read

Build a map and filter transducer little by little, compose them, and avoid talking about math.

# reduce()

const startWith = []
const oneItem = () => [1]
.reduce(doubleAndAppend, startWith)
const twoItems = () => [1, 2]
.reduce(doubleAndAppend, startWith)
const threeItems = () => [1, 2, 3]
.reduce(doubleAndAppend, startWith)

# doubleAndAppend()

const one = () =>
doubleAndAppend([], 1)
const two = () =>
doubleAndAppend([2], 2)
const three = () =>
doubleAndAppend([2, 4], 3)
function doubleAndAppend(accumulator, x) {
return [...accumulator, x * 2]
}

# double() + append()

const map = () =>
[1, 2, 3].reduce(doubleAndAppend, [])
function doubleAndAppend(accumulator, x) {
return append(accumulator, double(x))
}
function double(x) {
return x * 2
}
function append(accumulator, x) {
return [...accumulator, x]
}

# TransformAndAppend()

const doubleAndAppend = () =>
[1, 2, 3].reduce(
TransformAndAppend(double),
[]
)
function TransformAndAppend(transform) {
return (accumulator, x) =>
append(accumulator, transform(x))
}

# TransformAndBuild() / mapping transducer

const doubleAndAppend = () =>
[1, 2, 3].reduce(
TransformAndBuild(double)(append),
[]
)
function TransformAndBuild(transform) {
return build => {
return (accumulator, x) =>
build(accumulator, transform(x))
}
}

# appendIfDefined()

const excludeUndefined = () =>
[1, 2, undefined, 3].reduce(
TransformAndBuild(x => x)(
appendIfDefined
),
[]
)
function appendIfDefined(accumulator, x) {
return defined(x)
? [...accumulator, x]
: accumulator
}
function defined(x) {
return x !== undefined
}

# map() + filter() transducers

const doubleAndAppend =
map(double)(append)
const appendIfDefined =
filter(defined)(append)
const runMap = () =>
[1, 2, 3]
.reduce(doubleAndAppend, [])
const runFilter = () =>
[1, 2, undefined, 3]
.reduce(appendIfDefined, [])
function map(transform) {
return build => (acc, x) =>
build(acc, transform(x))
}
function filter(predicate) {
return build => (acc, x) =>
predicate(x) ? build(acc, x) : acc
}

# compose()

const doubleNumbers = map(double)
const onlyDefined = filter(defined)
const manually = () =>
[1, 2, undefined, 3].reduce(
onlyDefined(doubleNumbers(append)),
[]
)
const functionally = () =>
[1, 2, undefined, 3].reduce(
compose(
onlyDefined,
doubleNumbers
)(append),
[]
)
function compose(...fns) {
return x =>
fns.reduceRight((y, f) => {
const acc = f(y)
return acc
}, x)
}

Google it:

ðŸŒļ Array reduce 🌚 Array reduceRight 🌷 currying ðŸŒŧ function composition 🌞 high-order function ðŸŒđ spread rest 💐 transducer