Chaining is when you wrap a collection, define the pipeline, then extract the result in the end. If you know Lodash’s or Underscore.js’s
chain method, then you already know how to use it. In this post, we’ll look into how it works, and implement this function in a somewhat simplified way.
In contrast to Array#Extras, chaining does not require any existing functions present in the Arrays themselves. In fact, it is a generic concept that can work on any data types and can add any processing functions.
Chaining in action
The structure is wrapping, processing, and extracting the result. The first part is done by the
chain method, the second one is the collection pipeline itself (made of
filters, and the likes), and the final part is the
An example processing (Try it):
At first sight, it’s like magic. How to convert the
map and the
filter functions we already have to chainable steps?
The crux of the implementation is, obviously, the
chain method. This function gets a collection and returns an object that has all the processing functions and a fluent interface.
Let’s build this function step by step! (Try it)
Keep in mind that this is a simplified version, and you can make it in different ways.
The processing steps
The 0th step is to have the
filter functions ready! Let’s just pull the ones we’ve already used before.
The first step is to have a
chain function that gets a collection and returns an object:
Wrap the collection
The next step is to keep track of the current state of the collection, and provide a way to extract it with a
With only this in place, we’ve already made the wrapping and the extracting parts possible.
Attach the functions
The final step is to attach the processing functions in a chainable way. To do this, add a function with the same name, which gets some arguments and calls the processing functions (
filter in our case) with the current collection (
_currentArr) and the arguments.
To make it easier to digest, let’s consider only one function, the
map! To make it chainable, the
wrapper needs a function called “map” that:
- Calls the
mapimplementation with the collection and other arguments
- Updates the wrapped collection
- And returns the wrapper for a fluent interface
To add all the processing functions, use a loop:
The final code
Now that we know all the parts, putting them together results in a fully functional
And its usage:
In case you have a library of functions, just list them as processing functions and attach to the wrapper object. This way, the chain function can be customized with all the required processing steps.
But what if you use a third-party library, like Lodash or Underscore.js, and want to extend it?
Most libraries provide a way of adding functions. But they either modify the library itself, essentially polluting it, which we’ve already seen is not a good idea, or return a new
chain method that makes the syntax awkward.
There is one other thing that might seem wrong with this solution.
It’s on this line:
func.name returns the name of the function. But what if you use an aggressive minifier that changes the names of the functions in order to save some bytes? In that case,
func.name returns something other than “map” or “filter”.
As a result, be extra cautious with variable renaming when you use chaining.
Chaining is a great solution if you have a library of functions and want to provide a fluent interface for them.
But keep in mind the tradeoffs. First, chaining is hard to extend if you use a third-party solution. And second, it might interfere with the minifier.
In the next episode, we’ll look into a way of processing collections that is clear, easy to extend, and non-polluting.