chaining and function composition with lodash / underscore
TRANSCRIPT
![Page 1: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/1.jpg)
FUNCTIONS FUNCTIONS CHAININGCHAINING AND AND COMPOSITIONCOMPOSITION WITH _ WITH _
![Page 2: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/2.jpg)
@NICOESPEON@NICOESPEONhttp://nicoespeon.com
![Page 3: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/3.jpg)
BEFORE WE BEFORE WE DIVE INDIVE IN> A QUICK CHECK-UP> A QUICK CHECK-UP
![Page 4: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/4.jpg)
WHEN I SAY WHEN I SAY __I personally use (v3.0+)Works with too — for the most part \o/
lodashunderscore
![Page 5: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/5.jpg)
FUNCTIONAL PROGRAMMING?FUNCTIONAL PROGRAMMING?First-class functionsHigher order functionsPure functionsRecursionImmutability≠ imperative programmingbut !≠ OOP
![Page 6: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/6.jpg)
FUNCTIONS FUNCTIONS CHAININGCHAINING> WHAT IS THAT FOR?> WHAT IS THAT FOR?
![Page 7: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/7.jpg)
A CONCRETE CASE : MULTIPLE OPERATIONS (0)A CONCRETE CASE : MULTIPLE OPERATIONS (0)
this.set( "items", [ { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }, null, { id: "ebaaa82e", cepage: "gamay", type: "grape", quantity: 2 }, { id: "ee2bcc12", cepage: "viognier", type: "grape", quantity: 0 }] );
![Page 8: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/8.jpg)
A CONCRETE CASE : MULTIPLE OPERATIONS (1)A CONCRETE CASE : MULTIPLE OPERATIONS (1)
function getItems() { return _.compact( this.get( "items" ) );}
getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 },// { id: "ebaaa82e", cepage: "gamay", type: "grape", quantity: 2 },// { id: "ee2bcc12", cepage: "viognier", type: "grape", quantity: 0 }// ]
![Page 9: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/9.jpg)
A CONCRETE CASE : MULTIPLE OPERATIONS (2)A CONCRETE CASE : MULTIPLE OPERATIONS (2)
function getItems() { return _.reject( _.compact( this.get( "items" ) ), { quantity: 0 } );}
getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 },// { id: "ebaaa82e", cepage: "gamay", type: "grape", quantity: 2 }// ]
![Page 10: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/10.jpg)
A CONCRETE CASE : MULTIPLE OPERATIONS (3)A CONCRETE CASE : MULTIPLE OPERATIONS (3)function getItems() { return _.filter( _.reject( _.compact( this.get( "items" ) ), { quantity: 0 } ), { type: "juice" } );}
getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]
![Page 11: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/11.jpg)
A CONCRETE CASE : MULTIPLE OPERATIONS (3)A CONCRETE CASE : MULTIPLE OPERATIONS (3)// Better, really?function getItems() { var compactedItems = _.compact( this.get( "items" ) ); var positiveCompactedItems = _.reject( compactedItems, { quantity: 0 } );
return _.filter( positiveCompactedItems, { type: "juice" } );}
getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]
![Page 12: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/12.jpg)
_.CHAIN(_.CHAIN( VALUE VALUE ))Creates a lodash object that wraps value with explicitmethod chaining enabled.
_.chain( this.get( "items" ) );// => returns `LodashWrapper`
_.chain( this.get( "items" ) ).compact();// <=> `_.compact( this.get( "items" ) );`// BUT… returns `LodashWrapper` too!
// And so we can do ->_.chain( this.get( "items" ) ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) // … .map( doSomething );
![Page 13: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/13.jpg)
_.CHAIN(_.CHAIN( VALUE VALUE )) … … .VALUE().VALUE() ! !Executes the chained sequence to extract theunwrapped value.
_.chain( this.get( "items" ) ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) // Hum… still return `LodashWrapper` >_<
.value(); // And voilà \o/
// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]
![Page 14: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/14.jpg)
A CONCRETE CASE : MULTIPLE OPERATIONS (4)A CONCRETE CASE : MULTIPLE OPERATIONS (4)function getItems() { return _.chain( this.get( "items" ) ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) .value();}
getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]
function getItems() { return _( this.get( "items" ) ) // _( value ) === _.chain( value ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) .value();}
getItems();// => // [ // { id: "ae213aa4", cepage: "merlot", type: "juice", quantity: 3 }// ]
![Page 15: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/15.jpg)
WHAT WAS ALL THE WHAT WAS ALL THE FUSSFUSS ABOUT? ABOUT?> ADVANTAGES OF CHAINING> ADVANTAGES OF CHAINING
![Page 16: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/16.jpg)
PIPELINE / FLOWPIPELINE / FLOWfunction getItems() { return _( this.get( "items" ) ) .compact() .reject( isEmpty ) .filter( isJuice ) .map( parseText ) // … we construct the pipeline // flow is clear, readable! .value();}
Does that ring a bell?
![Page 17: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/17.jpg)
PIPELINE / FLOWPIPELINE / FLOWfunction makeItemAvailable( userID, index ) { return _findOneItem( userID, index ) .then( doSomethingClever ) .then( updateStatusAs( "available" ) ) .then( res.ok ) .catch( res.serverError );}
// You get the same idea with promises.
![Page 18: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/18.jpg)
function getBottles( options ) { // Ensure default options. options = _.defaults( {}, options, { isAppellationOnly: false } );
var bottlesWrapper = _( this.get( "bottles" ) ).map( parseText );
// …
// Dynamically build the pipeline. if( options.isAppellationOnly ) { bottlesWrapper = bottlesWrapper.pick( [ "appellation" ] ); }
// Nothing have been computed so far! return bottlesWrapper.value(); // evaluates when needed only!}
function getParsedBottlesWrapper() { return _( this.get( "bottles" ) ).map( parseText );}
function getBottles( options ) { // Ensure default options. options = _.defaults( {}, options, { isAppellationOnly: false } );
var bottlesWrapper = getParsedBottlesWrapper.call( this );
// Dynamically build the pipeline. if( options.isAppellationOnly ) { bottlesWrapper = bottlesWrapper.pick( [ "appellation" ] ); }
// Nothing have been computed so far! return bottlesWrapper.value(); // evaluates when needed only!}
LAZYLAZY EVALUATION EVALUATION
![Page 19: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/19.jpg)
LAZYLAZY EVALUATION EVALUATIONTo learn more about this >
http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/
![Page 20: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/20.jpg)
COMPOSITIONCOMPOSITION AND OTHER TRICKS AND OTHER TRICKS> TO BUILD > TO BUILD EFFICIENTEFFICIENT PIPELINES PIPELINES
![Page 21: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/21.jpg)
COMPOSITION ?COMPOSITION ?(f ⋅ g)(x) = f(g(x))
function add10( value ) { // f return 10 + value;}
function times3( value ) { // g return 3 * value;}
add10( times3( 10 ) ); // (f ∘ g)( 10 )// => 10 + ( 3 * 10 )// => 40
![Page 22: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/22.jpg)
COMPOSITION ?COMPOSITION ?
function add10( value ) { // f return 10 + value;}
function times3( value ) { // g return 3 * value;}
var times3AndAdd10 = _.compose( add10, times3 ); // f ∘ g
times3AndAdd10( 10 );// => 40
times3AndAdd10( 0 );// => 10
function add10( value ) { // f return 10 + value;}
function times3( value ) { // g return 3 * value;}
var times3AndAdd10 = _.flowRight( add10, times3 ); // f ∘ g
times3AndAdd10( 10 );// => 40
times3AndAdd10( 0 );// => 10
![Page 23: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/23.jpg)
_.FLOWRIGHT(_.FLOWRIGHT( [FUNCS] [FUNCS] ))Create a function that will return the result of everyfuncs invoked with the result of the preceding function,from the right to the left (= compose).
![Page 24: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/24.jpg)
_.FLOW(_.FLOW( [FUNCS] [FUNCS] ))
function add10( value ) { // f return 10 + value;}
function times3( value ) { // g return 3 * value;}
var times3AndAdd10 = _.flow( times3, add10 ); // f ∘ g
times3AndAdd10( 10 );// => 40
times3AndAdd10( 0 );// => 10
If _.flowRight is not of your taste.
![Page 25: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/25.jpg)
PARTIAL APPLICATION: PARTIAL APPLICATION: _.PARTIAL()_.PARTIAL()
function greet( greeting, name ) { return greeting + " " + name;}
var sayHelloTo = _.partial( greet, "Hello" );// returns a function with params partially set.
sayHelloTo( "Backbone" );// → "Hello Backbone"
![Page 26: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/26.jpg)
PARTIAL APPLICATIONPARTIAL APPLICATION
function _isCepageInRecipe( cepage, bottle ) { … }function _areBuildingsPartOfRecipe( buildings, bottle ) { … }
function hasMatchingBottles( cepage, buildings ) { var isCepageInRecipe = _.partial( _isCepageInRecipe, cepage ); var areBuildingsPartOfRecipe = _.partial( _areBuildingsPartOfRecipe, buildings );
return _( this.get( "bottles" ) ) .filter( isCepageInRecipe ) .any( areBuildingsPartOfRecipe );}
So you can chain in real life…
![Page 27: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/27.jpg)
function greet( greeting, name ) { return greeting + " " + name;}
var greetBackbone = _.partialRight( greet, "Backbone" );// returns a function with params partially set.
greetBackbone( "Hello" );// → "Hello Backbone"
_.PARTIALRIGHT()_.PARTIALRIGHT()
![Page 28: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/28.jpg)
// Not so smart params order here…function _isCepageInRecipe( bottle, cepage ) { … }function _areBuildingsPartOfRecipe( bottle, buildings ) { … }
// Not so smart params order here…function _isCepageInRecipe( bottle, cepage ) { … }function _areBuildingsPartOfRecipe( bottle, buildings ) { … }
function hasMatchingBottles( cepage, buildings ) { // Use `_` as a placeholder for not-yet-defined params! var isCepageInRecipe = _.partial( _isCepageInRecipe, _, cepage ); var areBuildingsPartOfRecipe = _.partial( _areBuildingsPartOfRecipe, _, buildings );
return _( this.get( "bottles" ) ) .filter( isCepageInRecipe ) .any( areBuildingsPartOfRecipe );}
// Not so smart params order here…function _isCepageInRecipe( bottle, cepage ) { … }function _areBuildingsPartOfRecipe( bottle, buildings ) { … }
function hasMatchingBottles( cepage, buildings ) { // Use `_` as a placeholder for not-yet-defined params! var isCepageInRecipe = _.partialRight( _isCepageInRecipe, cepage ); var areBuildingsPartOfRecipe = _.partialRight( _areBuildingsPartOfRecipe, buildings );
return _( this.get( "bottles" ) ) .filter( isCepageInRecipe ) .any( areBuildingsPartOfRecipe );}
PARTIAL APPLICATION: THE SWISS ARMY KNIFEPARTIAL APPLICATION: THE SWISS ARMY KNIFE
![Page 29: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/29.jpg)
COMPOSITION COMPOSITION VS.VS. CHAINING ? CHAINING ?_.flow is a tool to build higher order functionsIt can eventually replace simple chaining…… but is not meant to replace _.chain
function getJuices( items ) { return _( items ) .compact() .reject( { quantity: 0 } ) .filter( { type: "juice" } ) .value();}
// Flow equivalentvar getJuices = _.flow( _.partialRight( _.filter, { type: "juice" } ), _.partialRight( _.reject, { quantity: 0 } ), _.compact);
![Page 30: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/30.jpg)
BUT WAIT, THERE'S BUT WAIT, THERE'S MOREMORE> FEW > FEW SUBTLETIESSUBTLETIES AND OTHER CLASSICS AND OTHER CLASSICS
![Page 31: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/31.jpg)
NOTNOT EVERYTHING IS CHAINABLE EVERYTHING IS CHAINABLESome methods are: _.keys, _.map, _.push, _.pluck,_.union, … Others are not — by default: _.find, _.isNumber,_.reduce, _.sum, …
![Page 32: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/32.jpg)
NON-CHAINABLES METHODSNON-CHAINABLES METHODSfunction getJuiceTotalQuantity() { return _( this.get( "items" ) ) .compact() .filter( isJuice ) .pluck( "quantity" ) .sum(); // => return the sum // no need for `.value()` -> implicitly called}
More in the doc > https://lodash.com/docs#_
![Page 33: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/33.jpg)
_.PROTOTYPE.PLANT(_.PROTOTYPE.PLANT( VALUE VALUE ))Create a clone of the chain with the given value
var wrapper = _( [ 1, 2, null, 3 ] ).compact();
var otherWrapper = wrapper.plant( [ "a", null, "b", undefined ] );
wrapper.value();// => [ 1, 2, 3 ]
otherWrapper.value();// => [ "a", "b" ]
![Page 34: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/34.jpg)
_.PROTOTYPE.COMMIT()_.PROTOTYPE.COMMIT()Execute the chain and return a wrapper.
var array = [ 1, 2, 3 ];var wrapper = _( array ).push( 2 );
console.log( array );// => [ 1, 2, 3 ]// Nothing executed, nothing changed.
wrapper = wrapper.commit();
console.log( array );// => [ 1, 2, 3, 2 ]// Chain executed// `_.push()` mutated the original `array`
wrapper.without( 2 ).value();// => [ 1, 3 ]
![Page 35: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/35.jpg)
_.TAP(_.TAP( VALUE, INTERCEPTOR, [THISARG] VALUE, INTERCEPTOR, [THISARG] ))Invoke interceptor and return value.
_( [ 1, 2, null, 3 ] ) .compact() .tap( function ( value ) { console.log( "tapped ->", value ); } ) .push( 1 ) .value();
// => "tapped -> [ 1, 2, 3 ]"// => "[ 1, 2, 3, 1 ]"
"Tap" into the chain = very useful for debug!
![Page 36: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/36.jpg)
_.TAP(_.TAP( VALUE, INTERCEPTOR, [THISARG] VALUE, INTERCEPTOR, [THISARG] ))To log an intermediate value
_( [ 1, 2, null, 3 ] ) .compact() // Can use `console.log`, just don't forget to bind the context! .tap( console.log.bind( console ) ) .push( 1 ) .value();
// => "[ 1, 2, 3 ]"// => "[ 1, 2, 3, 1 ]"
![Page 37: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/37.jpg)
_.THRU(_.THRU( VALUE, INTERCEPTOR, [THISARG] VALUE, INTERCEPTOR, [THISARG] ))Invoke interceptor and return the value of interceptor.
_( [ 1, 2, null, 3 ] ) .compact() .thru( function ( value ) { console.log( "tapped ->", value );
// Don't forget to return a value for the chain. return value.reverse(); } ) .push( 0 ) .value();
// => "tapped -> [ 1, 2, 3 ]"// => "[ 3, 2, 1, 0 ]"
![Page 38: Chaining and function composition with lodash / underscore](https://reader031.vdocument.in/reader031/viewer/2022030304/5878a4231a28ab42588b6287/html5/thumbnails/38.jpg)
THANKS!THANKS! ANY QUESTION? ANY QUESTION?