Skip to content

Instantly share code, notes, and snippets.

@Nithanaroy
Last active November 24, 2017 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Nithanaroy/593c0f5689fd801636ebdc2fba2c292c to your computer and use it in GitHub Desktop.
Save Nithanaroy/593c0f5689fd801636ebdc2fba2c292c to your computer and use it in GitHub Desktop.
Performant chaining ops in JavaScript
/** Double all the even numbers in the given array **/
const isEven = (n) => n % 2 === 0;
const doubleIt = (n) => n * 2;
const finalAnswer = [1, 2, 3, 4]
.filter(isEven)
.map(doubleIt)
/** Double all the even numbers in the given array **/
const isEven = (n) => n % 2 === 0;
const doubleIt = (n) => n * 2;
const map = transformFunc => {
return (accumulator, value) => {
accumulator.push(transformFunc(value);
return accumulator;
}
}
const filter = predicateFunc => {
return (accumulator, value) => {
if ( predicateFunc( value ) ) {
accumulator.push(value);
}
return accumulator;
}
}
/* A step closer but still looping twice */
const finalAnswer = [1, 2, 3, 4]
.reduce(filter(isEven), [])
.reduce(map(doubleIt), [])
/* A function that takes a reducer as an argument and returns a reducer */
const filterTransducer = predicateFunc => reducer => {
return (accumulator, value) => {
if ( predicateFunc( value ) ) reducer(accumulator, value);
return accumulator;
}
}
const finalAnswer2 = [1, 2, 3, 4]
.reduce(filterTransducer( isEven )( map( doubleIt ) ), [])
/* A clearner way to call so that it is easy to understand */
const isEvenFilter = filterTranducer( isEven );
const isNot2Filter = filterTranducer( v => v !== 2 );
const doubleMap = map( doubleIt );
const finalAnswer3 = [1, 2, 3, 4].reduce( isNot2Filter( isEvenFilter( doubleMap ) ) ); // function composition
/* Similarly transducer for map */
const mapTransducer = transformFunc => reducer => {
return (accumulator, value) => {
reducer(accumulator, transformFunc(value));
return accumulator;
}
}
const doubleMapReducer = mapTransducer( doubleIt );
const pushReducer = ( accumulator, value ) => {
accumulator.push( value );
return accumulator;
}
// Compose filter and mapping function together while iterating over the array only once!
const finalAnswer4 = [1, 2, 3, 4]
.reduce( isNot2Filter( isEvenFilter( doubleMapReducer( pushReducer ) ) ) ); // function composition
/*
Secret behind this design
==========================
A function that returns the same datatype as output as it takes as input, composes naturally!
Here we are able to compose map and filter as they both reducer as input and return a reducer as output :)
Simpler e.g.:
myTrimFunc( myToUpperCaseFunc(" Hello World ") ); // here both take a string and return a string and can be composed any
number of times ;)
*/
@Nithanaroy
Copy link
Author

transducer.js only loops over the array, [1, 2, 3, 4] once for both filter and map with the chaining syntax.
costly_functional.js loops over the array twice and can be expensive for large arrays.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment