Table of Contents
Introduction 1.1
Everything is Reducible 1.2
Define Composition 1.3
Redefine Map 1.4
Redefine Filter 1.5
Composing Map & Filter 1.6
Transducers 1.7
1
Introduction
Advanced Functional JavaScript
Collection of code snippets to showcase functional aspects of JavaScript
Fun with Functions
1. Everything is Reducible
Reducer Function signature
length of an array
reverse an array
loop thru an array
2. Define Composition
3. Redefine map in different ways
classical map
map as a Reducing Function
map without dependency on input array
map without dependency on array behavior
4. Redefine filter in different ways
classical filter
filter as a Reducing Function
filter without dependency on input array
filter without dependency on array behavior
5. Compose Map & Filter
classical map ⋅ filter combo
map ⋅ filter as a Composable Reducing Function
6. Transducers
recap map & filter w/o dependencies
create mapping & filtering transducers
compose mapping & filtering transducers
use mapping & filtering transducers as Reducing Functions
2
Everything is Reducible
Reducer Function signature
A Reducer Function takes inputs as ( current state , action ) and computes
the next state to return back.
var genericReducerFn = (current_state, input) => next_state;
Example identity reducer shown below returns the same state.
function identity(current_state, action) {
var next_state = current_state;
return next_state;
};
/*---OR---*/
var identity = (current_state, action) => current_state;
Reducer Functions are powerful because they can be passed to a generic
reduce aggregator along with an initial state and a set of actions to
compute the final state
[actions].reduce(reducerFn, initial_state);
//-> final_state
Length of an array
var lengthReducer = (current_length, item) => current_length + 1
;
[4, 3, 2, 1, 0].reduce(lengthReducer, 0);
//-> 5
Reverse an array
3
Everything is Reducible
var arrayPrepender = (current_array, item) => {
var new_array = current_array.slice();
new_array.unshift(item);
return new_array;
};
[1, 2, 3, 4].reduce(arrayPrepender, [ ]);
//-> [4, 3, 2, 1]
Loop thru an array
var looper = (current_state, action) => console.log(action);
[2, 4, 6].reduce(looper, null);
/* 2
* 4
* 6
*/
4
Define Composition
compose(. . .)
compose(h,g,f)(x) = h(g(f(x)))
/**
* usage:
* let inc_one = (x) => x + 1;
* let inc_two = (x) => x + 2;
* let inc_three = compose(inc_one, inc_two);
* inc_three(5); //-> 8
*/
function compose() {
var argsOuter = arguments;
return function() {
var arg = arguments;
var out = undefined;
for(var i = argsOuter.length-1; i >= 0; i--) {
if (out === undefined) {
out = argsOuter[i].apply(null, arg)
} else {
out = argsOuter[i].call(null, out);
}
}
return out;
}
}
let div_by_three = (x) => x/3;
let div_by_five = (x) => x/5;
let div_by_fifteen = compose(div_by_three, div_by_five);
div_by_fifteen(2);
//-> 0.13333333333333333
5
Define Composition
6
Redefine Map
Classical map
[1, 2, 3].map(x => x+1);
//-> [2, 3, 4]
map as a Reducing Function
var map = (fn, arr) => {
return arr.reduce(function(accumulator, input) {
let a = accumulator.slice();
a.push(fn(input));
return a;
}, [ ]);
};
map(x => x+1, [1, 2, 3]);
//-> [2, 3, 4]
Remove map dependency on array input
var map = (fn) => {
return (accumulator, input) => {
let a = accumulator.slice();
a.push(fn(input));
return a;
};
};
[1, 2, 3].reduce(map(x => x+1), [ ]);
//-> [2, 3, 4]
Remove map dependency on accumulator
behavior
7
Redefine Map
var map = (fn) => {
return (reducer) => {
return (accumulator, input) => {
return reducer(accumulator, fn(input));
};
};
};
var arrayConcat = (arr, item) => {
let a = arr.slice();
a.push(item);
return a;
};
[1, 2, 3].reduce(map(x => x+1)(arrayConcat), [ ]);
//-> [2, 3, 4]
8
Redefine Filter
Classical filter
[1, 2, 3, 4].filter(x => x%2 === 0);
//-> [2, 4]
filter as a Reducing Function
var filter = (predicate, arr) => {
return arr.reduce(function(accumulator, input) {
let a = accumulator.slice();
if(predicate(input)) {
a.push(input);
}
return a;
}, [ ]);
};
filter(x => x%2 === 0, [1, 2, 3, 4]);
//-> [2, 4]
Remove filter dependency on array input
var filter = (predicate) => {
return (accumulator, input) => {
let a = accumulator.slice();
if(predicate(input)) {
a.push(input);
}
return a;
};
};
[1, 2, 3, 4].reduce(filter(x => x%2 === 0), [ ]);
//-> [2, 4]
9
Redefine Filter
Remove filter dependency on accumulator
behavior
var filter = (predicate) => {
return (reducer) => {
return (accumulator, input) => {
return predicate(input)?
reducer(accumulator, input):
accumulator;
};
};
};
var arrayConcat = (arr, item) => {
let a = arr.slice();
a.push(item);
return a;
};
[1, 2, 3, 4].reduce(filter(x => x%2 === 0)(arrayConcat), [ ]);
//-> [2, 3, 4]
10
Composing Map & Filter
Classical map & filter
[1, 2, 3, 4].map(x => x+1).filter(x => x<4);
//-> [2, 3]
Compose map & filter using new definitions
var mapFilter = compose(
map(x => x+1),
filter(x => x<4)
);
function arrayConcat(arr, item) {
arr.push(item);
return arr;
};
[1, 2, 3, 4].reduce(mapFilter(arrayConcat), [ ]);
//-> [2, 3]
11
Transducers
Transducers
Mapping & Filtering functions defined with no array behavior dependencies
can be called transducers.
We can easily compose mapping & filtering transducers and use them as
Reducing Functions
Recap map & filter without dependencies
var map = (fn) => {
return (reducer) => {
return (accumulator, input) => {
return reducer(accumulator, fn(input));
};
};
};
var filter = (predicate) => {
return (reducer) => {
return (accumulator, input) => {
return predicate(input)?
reducer(accumulator, input):
accumulator;
};
};
};
Create mapping & filtering transducers
var mappingTransducer = map(x => x+1);
var filteringTransducer = filter(x => x<4);
12
Transducers
Compose mapping & filtering transducers
var mapFilterTransducer = compose(
mappingTransducer,
filteringTransducer
);
Use mapping & filtering transducers
function arrayConcat(arr, item) {
arr.push(item);
return arr;
};
[1, 2, 3, 4].reduce(mapFilterTransducer(arrayConcat), [ ]);
//-> [2, 3]
References
Transducers are coming
CSP and transducers in JavaScript
Transducers.js: A JavaScript Library for Transformation of Data
13