Reputation: 4471
As we all know, lodash-es
is built with a more modular syntax for supporting tree shaking by build tools.
However, chain
related features means some functions are attached to a object/prototype chain.
I can see chain
is published with lodash-es
, but I am not sure how to use it with proper imports with other chained method.
A usecase may look like this:
import { chain } from 'lodash-es'
export function double(input) {
return chain(input)
.without(null)
.map(val => val * 2)
.value()
.join(', ')
}
The point is not about how is chain
imported, but about how are other chained
functions imported.
Upvotes: 21
Views: 11585
Reputation: 123
lodash-es/flow
I went with another approach, which doesn't rely on modifying lodash internals, and still keeps tree shaking.
import { flow, map, flatten, sortBy } from "lodash-es";
export const f = <V, I, R>(func: (v: V, i: I) => R, i: I) => (v: V) => func(v, i)
const value = flow([
f(map, x => [x, x*2]),
flatten,
f(sortBy, x => x)
])([3,1,2])
The idea is to manually wrap each function that normally takes (value, iteratee)
parameters with a higher-order function f()
that also accepts the iteratee, and outputs a value-first equivalent. In other words, you convert the function to its lodash/fp
behavior.
This adds a little extra syntax, but you might find it readable enough.
Upvotes: 1
Reputation: 28062
I found simpler, but tricker answer on how to build your own chain.
import * as ld, { wrapperLodash as _ } from 'lodash-es'
ld.mixin(_, {
chain: ld.chain,
map: ld.map
})
_.prototype.value = ld.value
const emails = _.chain(users)
.map('email')
.value()
Upvotes: 1
Reputation: 11396
EDIT: as pointed out by Snook, there has been work on a github issue on this subject. So I've added this to my answer. Go to Flow solution for the previous answer (which is as good IMHO).
import map from 'lodash-es/map';
import filter from 'lodash-es/filter';
import mapValues from 'lodash-es/mapValues';
import toPairs from 'lodash-es/toPairs';
import orderBy from 'lodash-es/orderBy';
import groupBy from 'lodash-es/groupBy';
import sortBy from 'lodash-es/sortBy';
// just add here the lodash functions you want to support
const chainableFunctions = {
map,
filter,
toPairs,
orderBy,
groupBy,
sortBy,
};
export const chain = (input) => {
let value = input;
const wrapper = {
...mapValues(
chainableFunctions,
(f) => (...args) => {
// lodash always puts input as the first argument
value = f(value, ...args);
return wrapper;
},
),
value: () => value,
};
return wrapper;
};
There is also a TypeScript version available at lodash/lodash#3298.
You can't, chain
needs to bundle all (or most) lodash's functions.
You can use flow
though. Here is an example of converting this:
import _ from "lodash";
_.chain([1, 2, 3])
.map(x => [x, x*2])
.flatten()
.sort()
.value();
into this:
import map from "lodash/fp/map";
import flatten from "lodash/fp/flatten";
import sortBy from "lodash/fp/sortBy";
import flow from "lodash/fp/flow";
flow(
map(x => [x, x*2]),
flatten,
sortBy(x => x)
)([1,2,3]);
This example (and more) come from this article.
Upvotes: 16
Reputation: 51886
In chain.js
, you see the first line is
import lodash from './wrapperLodash.js';
If we go to that file, we'll find a long explanation about how chaining is implemented using lazy evaluation that can shortcut iteratees until the call to value()
. Below that is an exported helper function defined like this:
function lodash(value) {
if (isObjectLike(value) && !isArray(value) && !(value instanceof LazyWrapper)) {
if (value instanceof LodashWrapper) {
return value;
}
if (hasOwnProperty.call(value, '__wrapped__')) {
return wrapperClone(value);
}
}
return new LodashWrapper(value);
}
Going back to chain.js
, we see how that is used in the chain()
function:
function chain(value) {
var result = lodash(value);
result.__chain__ = true;
return result;
}
Essentially, chain()
checks the input to make sure it's not already a wrapped value, and if it is, it either returns the value if it's an instance of the correct class, or it returns a new wrapped value.
There are no methods attached to any native prototype chains in this implementation, but it does create a new class called LodashWrapper
that wraps the input object with lodash
functionality and lazy evaluation optimizations.
I believe the correct import
statement to apply tree-shaking would be
import chain from 'lodash-es/chain'
This imports the same module to the same variable as the import
statement used in the question, but the difference is that running import { chain } from 'lodash-es'
evaluates all of the imports in lodash.js
, whereas my import
method only touches the chain.js
file and whatever its necessary dependencies are in wrapperLodash.js
.
Upvotes: 1