15. WHAT WAS ALL THEWHAT WAS ALL THE FUSSFUSS ABOUT?ABOUT?
> ADVANTAGES OF CHAINING> ADVANTAGES OF CHAINING
16. PIPELINE / FLOWPIPELINE / FLOW
function 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?
17. PIPELINE / FLOWPIPELINE / FLOW
function 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.
18. 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 EVALUATIONEVALUATION
24. _.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.
25. 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"
26. 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…
27. 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()
28. // 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
29. COMPOSITIONCOMPOSITION VS.VS. CHAINING ?CHAINING ?
_.flow is a tool to build higher order functions
It 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 equivalent
var getJuices = _.flow(
_.partialRight( _.filter, { type: "juice" } ),
_.partialRight( _.reject, { quantity: 0 } ),
_.compact
);
30. BUT WAIT, THERE'SBUT WAIT, THERE'S MOREMORE
> FEW> FEW SUBTLETIESSUBTLETIES AND OTHER CLASSICSAND OTHER CLASSICS
31. NOTNOT EVERYTHING IS CHAINABLEEVERYTHING IS CHAINABLE
Some methods are: _.keys, _.map, _.push, _.pluck,
_.union, …
Others are not — by default: _.find, _.isNumber,
_.reduce, _.sum, …
32. NON-CHAINABLES METHODSNON-CHAINABLES METHODS
function 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#_
33. _.PROTOTYPE.PLANT(_.PROTOTYPE.PLANT( VALUEVALUE ))
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" ]