SlideShare une entreprise Scribd logo
1  sur  38
Télécharger pour lire hors ligne
FUNCTIONSFUNCTIONS CHAININGCHAINING ANDAND
COMPOSITIONCOMPOSITION WITH _WITH _
@NICOESPEON@NICOESPEON
http://nicoespeon.com
BEFORE WEBEFORE WE DIVE INDIVE IN
> A QUICK CHECK-UP> A QUICK CHECK-UP
WHEN I SAYWHEN I SAY __
I personally use (v3.0+)
Works with too — for the most part o/
lodash
underscore
FUNCTIONAL PROGRAMMING?FUNCTIONAL PROGRAMMING?
First-class functions
Higher order functions
Pure functions
Recursion
Immutability
≠ imperative programming
but !≠ OOP
FUNCTIONSFUNCTIONS CHAININGCHAINING
> WHAT IS THAT FOR?> WHAT IS THAT FOR?
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 }
] );
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 }
// ]
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 }
// ]
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 }
// ]
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 }
// ]
_.CHAIN(_.CHAIN( VALUEVALUE ))
Creates a lodash object that wraps value with explicit
method 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 );
_.CHAIN(_.CHAIN( VALUEVALUE )) …… .VALUE().VALUE() !!
Executes the chained sequence to extract the
unwrapped 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 }
// ]
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 }
// ]
WHAT WAS ALL THEWHAT WAS ALL THE FUSSFUSS ABOUT?ABOUT?
> ADVANTAGES OF CHAINING> ADVANTAGES OF CHAINING
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?
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.
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
LAZYLAZY EVALUATIONEVALUATION
To learn more about this >
http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/
COMPOSITIONCOMPOSITION AND OTHER TRICKSAND OTHER TRICKS
> TO BUILD> TO BUILD EFFICIENTEFFICIENT PIPELINESPIPELINES
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
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
_.FLOWRIGHT(_.FLOWRIGHT( [FUNCS][FUNCS] ))
Create a function that will return the result of every
funcs invoked with the result of the preceding function,
from the right to the left (= compose).
_.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.
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"
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…
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()
// 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
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
);
BUT WAIT, THERE'SBUT WAIT, THERE'S MOREMORE
> FEW> FEW SUBTLETIESSUBTLETIES AND OTHER CLASSICSAND OTHER CLASSICS
NOTNOT EVERYTHING IS CHAINABLEEVERYTHING IS CHAINABLE
Some methods are: _.keys, _.map, _.push, _.pluck,
_.union, …
Others are not — by default: _.find, _.isNumber,
_.reduce, _.sum, …
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#_
_.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" ]
_.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 ]
_.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!
_.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 ]"
_.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 ]"
THANKS!THANKS! ANY QUESTION?ANY QUESTION?

Contenu connexe

Tendances

Magicke metody v Pythonu
Magicke metody v PythonuMagicke metody v Pythonu
Magicke metody v Pythonu
Jirka Vejrazka
 

Tendances (20)

Beyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS AppsBeyond the DOM: Sane Structure for JS Apps
Beyond the DOM: Sane Structure for JS Apps
 
JQuery Presentation
JQuery PresentationJQuery Presentation
JQuery Presentation
 
Drupal 8 database api
Drupal 8 database apiDrupal 8 database api
Drupal 8 database api
 
Selectors and normalizing state shape
Selectors and normalizing state shapeSelectors and normalizing state shape
Selectors and normalizing state shape
 
TDD in the wild
TDD in the wildTDD in the wild
TDD in the wild
 
Funcitonal Swift Conference: The Functional Way
Funcitonal Swift Conference: The Functional WayFuncitonal Swift Conference: The Functional Way
Funcitonal Swift Conference: The Functional Way
 
Drupal7 dbtng
Drupal7  dbtngDrupal7  dbtng
Drupal7 dbtng
 
Dependency Injection
Dependency InjectionDependency Injection
Dependency Injection
 
Implementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with SlickImplementing a many-to-many Relationship with Slick
Implementing a many-to-many Relationship with Slick
 
Powerful JavaScript Tips and Best Practices
Powerful JavaScript Tips and Best PracticesPowerful JavaScript Tips and Best Practices
Powerful JavaScript Tips and Best Practices
 
Js types
Js typesJs types
Js types
 
Jquery Fundamentals
Jquery FundamentalsJquery Fundamentals
Jquery Fundamentals
 
ActionScript3 collection query API proposal
ActionScript3 collection query API proposalActionScript3 collection query API proposal
ActionScript3 collection query API proposal
 
Ian 20150116 java script oop
Ian 20150116 java script oopIan 20150116 java script oop
Ian 20150116 java script oop
 
Magicke metody v Pythonu
Magicke metody v PythonuMagicke metody v Pythonu
Magicke metody v Pythonu
 
Scala introduction
Scala introductionScala introduction
Scala introduction
 
PHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php frameworkPHP 5.3 and Lithium: the most rad php framework
PHP 5.3 and Lithium: the most rad php framework
 
Javascript And J Query
Javascript And J QueryJavascript And J Query
Javascript And J Query
 
Everything About PowerShell
Everything About PowerShellEverything About PowerShell
Everything About PowerShell
 
Patterns for slick database applications
Patterns for slick database applicationsPatterns for slick database applications
Patterns for slick database applications
 

En vedette

En vedette (6)

Ramda lets write declarative js
Ramda   lets write declarative jsRamda   lets write declarative js
Ramda lets write declarative js
 
Javascript State of the Union 2015
Javascript State of the Union 2015Javascript State of the Union 2015
Javascript State of the Union 2015
 
Ramadan Slides
Ramadan SlidesRamadan Slides
Ramadan Slides
 
reveal.js 3.0.0
reveal.js 3.0.0reveal.js 3.0.0
reveal.js 3.0.0
 
Designing Teams for Emerging Challenges
Designing Teams for Emerging ChallengesDesigning Teams for Emerging Challenges
Designing Teams for Emerging Challenges
 
Build Features, Not Apps
Build Features, Not AppsBuild Features, Not Apps
Build Features, Not Apps
 

Similaire à Chaining and function composition with lodash / underscore

Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
wpnepal
 
The Beauty Of Java Script V5a
The Beauty Of Java Script V5aThe Beauty Of Java Script V5a
The Beauty Of Java Script V5a
rajivmordani
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Paulo Ragonha
 

Similaire à Chaining and function composition with lodash / underscore (20)

Chaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscoreChaining et composition de fonctions avec lodash / underscore
Chaining et composition de fonctions avec lodash / underscore
 
Building Lithium Apps
Building Lithium AppsBuilding Lithium Apps
Building Lithium Apps
 
Avinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPressAvinash Kundaliya: Javascript and WordPress
Avinash Kundaliya: Javascript and WordPress
 
The Beauty of Java Script
The Beauty of Java ScriptThe Beauty of Java Script
The Beauty of Java Script
 
The Beauty Of Java Script V5a
The Beauty Of Java Script V5aThe Beauty Of Java Script V5a
The Beauty Of Java Script V5a
 
Getting the Most Out of jQuery Widgets
Getting the Most Out of jQuery WidgetsGetting the Most Out of jQuery Widgets
Getting the Most Out of jQuery Widgets
 
PHP PPT FILE
PHP PPT FILEPHP PPT FILE
PHP PPT FILE
 
Fewd week5 slides
Fewd week5 slidesFewd week5 slides
Fewd week5 slides
 
ES6: The Awesome Parts
ES6: The Awesome PartsES6: The Awesome Parts
ES6: The Awesome Parts
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Unittests für Dummies
Unittests für DummiesUnittests für Dummies
Unittests für Dummies
 
JavaScript Functions
JavaScript FunctionsJavaScript Functions
JavaScript Functions
 
Rails is not just Ruby
Rails is not just RubyRails is not just Ruby
Rails is not just Ruby
 
Aplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com BackboneAplicacoes dinamicas Rails com Backbone
Aplicacoes dinamicas Rails com Backbone
 
Advanced redux
Advanced reduxAdvanced redux
Advanced redux
 
Doctrine For Beginners
Doctrine For BeginnersDoctrine For Beginners
Doctrine For Beginners
 
05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards05 JavaScript #burningkeyboards
05 JavaScript #burningkeyboards
 
Spring data iii
Spring data iiiSpring data iii
Spring data iii
 
Stop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScriptStop Making Excuses and Start Testing Your JavaScript
Stop Making Excuses and Start Testing Your JavaScript
 

Plus de Nicolas Carlo

Plus de Nicolas Carlo (10)

The Secrets of Hexagonal Architecture
The Secrets of Hexagonal ArchitectureThe Secrets of Hexagonal Architecture
The Secrets of Hexagonal Architecture
 
Hexagonal architecture & Elixir
Hexagonal architecture & ElixirHexagonal architecture & Elixir
Hexagonal architecture & Elixir
 
À la découverte des Observables - HumanTalks Paris 2017
À la découverte des Observables - HumanTalks Paris 2017À la découverte des Observables - HumanTalks Paris 2017
À la découverte des Observables - HumanTalks Paris 2017
 
À la découverte des observables
À la découverte des observablesÀ la découverte des observables
À la découverte des observables
 
Testing Marionette.js Behaviors
Testing Marionette.js BehaviorsTesting Marionette.js Behaviors
Testing Marionette.js Behaviors
 
Les générateurs de code, pour se simplifier la vie au quotidien
Les générateurs de code, pour se simplifier la vie au quotidienLes générateurs de code, pour se simplifier la vie au quotidien
Les générateurs de code, pour se simplifier la vie au quotidien
 
Kanban et Game Development avec Trello
Kanban et Game Development avec TrelloKanban et Game Development avec Trello
Kanban et Game Development avec Trello
 
Plop : un micro-générateur pour se simplifier la vie au quotidien
Plop : un micro-générateur pour se simplifier la vie au quotidienPlop : un micro-générateur pour se simplifier la vie au quotidien
Plop : un micro-générateur pour se simplifier la vie au quotidien
 
Tester ses Behaviors Marionette.js
Tester ses Behaviors Marionette.jsTester ses Behaviors Marionette.js
Tester ses Behaviors Marionette.js
 
Comment organiser un gros projet backbone
Comment organiser un gros projet backboneComment organiser un gros projet backbone
Comment organiser un gros projet backbone
 

Dernier

pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdfpdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
JOHNBEBONYAP1
 
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
@Chandigarh #call #Girls 9053900678 @Call #Girls in @Punjab 9053900678
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
nilamkumrai
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
Call Girls In Delhi Whatsup 9873940964 Enjoy Unlimited Pleasure
 
Lucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRL
Lucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRLLucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRL
Lucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRL
imonikaupta
 
💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
nirzagarg
 
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Chandigarh Call girls 9053900678 Call girls in Chandigarh
 
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
nirzagarg
 

Dernier (20)

pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdfpdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
pdfcoffee.com_business-ethics-q3m7-pdf-free.pdf
 
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting High Prof...
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting  High Prof...VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting  High Prof...
VIP Model Call Girls Hadapsar ( Pune ) Call ON 9905417584 Starting High Prof...
 
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
📱Dehradun Call Girls Service 📱☎️ +91'905,3900,678 ☎️📱 Call Girls In Dehradun 📱
 
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
( Pune ) VIP Baner Call Girls 🎗️ 9352988975 Sizzling | Escorts | Girls Are Re...
 
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
valsad Escorts Service ☎️ 6378878445 ( Sakshi Sinha ) High Profile Call Girls...
 
Sarola * Female Escorts Service in Pune | 8005736733 Independent Escorts & Da...
Sarola * Female Escorts Service in Pune | 8005736733 Independent Escorts & Da...Sarola * Female Escorts Service in Pune | 8005736733 Independent Escorts & Da...
Sarola * Female Escorts Service in Pune | 8005736733 Independent Escorts & Da...
 
APNIC Policy Roundup, presented by Sunny Chendi at the 5th ICANN APAC-TWNIC E...
APNIC Policy Roundup, presented by Sunny Chendi at the 5th ICANN APAC-TWNIC E...APNIC Policy Roundup, presented by Sunny Chendi at the 5th ICANN APAC-TWNIC E...
APNIC Policy Roundup, presented by Sunny Chendi at the 5th ICANN APAC-TWNIC E...
 
Trump Diapers Over Dems t shirts Sweatshirt
Trump Diapers Over Dems t shirts SweatshirtTrump Diapers Over Dems t shirts Sweatshirt
Trump Diapers Over Dems t shirts Sweatshirt
 
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
Call Girls Sangvi Call Me 7737669865 Budget Friendly No Advance BookingCall G...
 
"Boost Your Digital Presence: Partner with a Leading SEO Agency"
"Boost Your Digital Presence: Partner with a Leading SEO Agency""Boost Your Digital Presence: Partner with a Leading SEO Agency"
"Boost Your Digital Presence: Partner with a Leading SEO Agency"
 
WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)
WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)
WhatsApp 📞 8448380779 ✅Call Girls In Mamura Sector 66 ( Noida)
 
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
2nd Solid Symposium: Solid Pods vs Personal Knowledge Graphs
 
Russian Call Girls Pune (Adult Only) 8005736733 Escort Service 24x7 Cash Pay...
Russian Call Girls Pune  (Adult Only) 8005736733 Escort Service 24x7 Cash Pay...Russian Call Girls Pune  (Adult Only) 8005736733 Escort Service 24x7 Cash Pay...
Russian Call Girls Pune (Adult Only) 8005736733 Escort Service 24x7 Cash Pay...
 
Lucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRL
Lucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRLLucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRL
Lucknow ❤CALL GIRL 88759*99948 ❤CALL GIRLS IN Lucknow ESCORT SERVICE❤CALL GIRL
 
💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
💚😋 Salem Escort Service Call Girls, 9352852248 ₹5000 To 25K With AC💚😋
 
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
Low Sexy Call Girls In Mohali 9053900678 🥵Have Save And Good Place 🥵
 
APNIC Updates presented by Paul Wilson at ARIN 53
APNIC Updates presented by Paul Wilson at ARIN 53APNIC Updates presented by Paul Wilson at ARIN 53
APNIC Updates presented by Paul Wilson at ARIN 53
 
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men  🔝mehsana🔝   Escorts...
➥🔝 7737669865 🔝▻ mehsana Call-girls in Women Seeking Men 🔝mehsana🔝 Escorts...
 
Call Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service Available
Call Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service AvailableCall Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service Available
Call Girls Ludhiana Just Call 98765-12871 Top Class Call Girl Service Available
 
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
Shikrapur - Call Girls in Pune Neha 8005736733 | 100% Gennuine High Class Ind...
 

Chaining and function composition with lodash / underscore

  • 3. BEFORE WEBEFORE WE DIVE INDIVE IN > A QUICK CHECK-UP> A QUICK CHECK-UP
  • 4. WHEN I SAYWHEN I SAY __ I personally use (v3.0+) Works with too — for the most part o/ lodash underscore
  • 5. FUNCTIONAL PROGRAMMING?FUNCTIONAL PROGRAMMING? First-class functions Higher order functions Pure functions Recursion Immutability ≠ imperative programming but !≠ OOP
  • 6. FUNCTIONSFUNCTIONS CHAININGCHAINING > WHAT IS THAT FOR?> WHAT IS THAT FOR?
  • 7. 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 } ] );
  • 8. 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 } // ]
  • 9. 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 } // ]
  • 10. 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 } // ]
  • 11. 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 } // ]
  • 12. _.CHAIN(_.CHAIN( VALUEVALUE )) Creates a lodash object that wraps value with explicit method 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 );
  • 13. _.CHAIN(_.CHAIN( VALUEVALUE )) …… .VALUE().VALUE() !! Executes the chained sequence to extract the unwrapped 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 } // ]
  • 14. 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 } // ]
  • 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
  • 19. LAZYLAZY EVALUATIONEVALUATION To learn more about this > http://filimanjaro.com/blog/2014/introducing-lazy-evaluation/
  • 20. COMPOSITIONCOMPOSITION AND OTHER TRICKSAND OTHER TRICKS > TO BUILD> TO BUILD EFFICIENTEFFICIENT PIPELINESPIPELINES
  • 21. 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
  • 22. 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
  • 23. _.FLOWRIGHT(_.FLOWRIGHT( [FUNCS][FUNCS] )) Create a function that will return the result of every funcs invoked with the result of the preceding function, from the right to the left (= compose).
  • 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" ]
  • 34. _.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 ]
  • 35. _.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!
  • 36. _.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 ]"
  • 37. _.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 ]"