The Problem

But there was trouble in paradise. As time went on, more functions were added to lodash; people realized that maybe they didn’t need all that functionality. People also realized they’d like to add their own functionality: new methods as part of _.chain; but they found that these new methods didn’t always play well with one another.

It Can Get Big

Web applications that include lodash can be big. Today two versions of lodash (v4) exist: the kitchen sink with everything and a more minimal build with less functionality; these packages have a build size (min+gz) of approx. 21 KiB and 4 KiB respectively. Finding the right balance can be hard, and ideally you’d like to just import what you need, since most apps will use only a fraction of the total functionality in lodash. However, because of the way _.chain works, it can be rather ineloquent to import the particular bit you need.

This is because the _.chain function takes, as input, a (typically array-like) object and returns, as output, a chain object whose methods correspond to the current chainable methods in lodash.

_.chain = (array) => wrap(array, _); // Rough concept of chain

Because of this you either have to import lodash wholesale, or extend the global lodash object with the methods you want.

Wholesale import is the most common case, resulting in bringing the entirety of lodash into your build. It’s the most heavy-handed but also the most convenient:

import _ from "lodash"; // Import everything. _.chain([1,2,3]).map(x => x+1).value(); // Use the methods.

To avoid the larger build sizes associated with using a global import, you can mix your desired methods into an initially empty global lodash object:

import chain from "lodash/chain";

import value from "lodash/value";

import map from "lodash/map";

import mixin from "lodash/mixin";

import _ from "lodash/wrapperLodash"; // Add the methods you want. The object generated by chain() will

// now have these methods.

mixin(_, {map: map, chain: chain, value: value}); _.chain([1,2,3]).map(x => x+1).value(); // Use the methods.

This is obviously an extremely convoluted process, having to import the empty wrapper prototype and then bind your methods before calling them. Not to mention, modifying globals is never a good thing. There is a less convoluted mechanism provided by lodash called _.runInContext, but it’s most sadly available only in the monolithic build. So we’re kind of stuck between a rock and a hard place.

It’s Not Nice to Extend

Adding your own processing methods is less than ideal; they must be added or injected into this wrapped chain object. There are a handful of ways that this can be achieved from the downright dirty to the almost acceptable.

Let’s give ourselves a fictitious function we want to use that tests an array of strings and returns those entries that have vowels in them:

import filter from "lodash/filter"; const vowels = (array) => filter(array, str => /[aeiou]/i.test(str)

As talked about previously, you can use _.mixin in both monolithic and modular fashions. The monolithic version has a fairly nice implementation going for it:

import _ from "lodash"; _.mixin({vowels: vowels}); _.chain(['ab','cd','ef']).vowels().value(); // Use the methods.

That wasn’t so bad, but when turning to the modular approach, things start to get particularly messy:

import chain from "lodash/chain";

import value from "lodash/value";

import map from "lodash/map";

import mixin from "lodash/mixin";

import _ from "lodash/wrapperLodash"; mixin(_, {

chain: chain,

value: value,

map: map, // Add existing lodash methods you need.

vowels: vowels, // Add your own lodash methods.

}); _.chain(['ab','cd','ef']).vowels().value(); // Use the methods.

The cleanest “non-polluting” way to use _.chain is with _.tap or _.thru. While it doesn’t mutate global objects and preserves the flow-like structure, it still requires every function be wrapped in one of these methods. Again, in both monolithic and modular forms:

import _ from "lodash"; _.chain(['ab','cd','ef']).thru(vowels).value();

And again the modular version gets fairly obtuse:

import chain from "lodash/chain";

import value from "lodash/value";

import mixin from "lodash/mixin";

import thru from "lodash/thru";

import _ from "lodash/wrapperLodash"; // Have one file that only imports core lodash methods you need.

mixin(_, {

chain: chain,

value: value,

thru: thru,

}); chain(['ab','cd','ef']).thru(vowels).value();

Neither of the two approaches, (with respect to their modular versions), is particularly attractive. So what can we do about this?

Much Ado About Something

There are some existing solutions to these issues, from the simple “just don’t use chain” to the nuclear “build a whole new library” (trine). Fortunately it’s also possible to solve this problem in a simple, sane manner with the nice functional programming tools that are already available in lodash toolbox.