Last active
November 24, 2017 21:55
-
-
Save Nithanaroy/593c0f5689fd801636ebdc2fba2c292c to your computer and use it in GitHub Desktop.
Performant chaining ops in JavaScript
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 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 ;) | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.