Reputation: 1381
If I have a simple lodash chain that maps then filters an array:
lodash.chain(myarray)
.map(item=>{
if (item === 'some-condition') return [item];
})
.filter(item=>!!item)
.value();
Obviously, this is a made-up example but it relates to something simple I do all the time. Basically, a array map where some maps are not possible so 'undefined' is returned. I then filter-out all the undefined values.
Since, it is used quite lot, it makes sense to mixin it into my lodash.
So:
const lodash = _.runInContext();
function mapFilter(ary, iterator) {
return lodash.chain(ary)
.map(iterator)
.filter(item=>!!item)
.value()
}
lodash.mixin(lodash, mapFilter, {chain:true});
Obviously, we could just do the whole thing without lodash but normally, it might be part of a bigger chain. In theory, the lazy-evaluation makes it quicker.
What I really want is to tap into the current chain (if there is one) in my mixed-in method. Otherwise, I am losing the lazy-evaluation by calling value() twice.
So, if I had a longer chain:
lodash.chain(myarray)
.mapFilter( // do something) // my bespoke chainable method
.map( // do something else )
.sort()
.value();
I'd like to use the current chain (when there is one) in my bespoke method. Something like this:
// This is made-up and does not work!
const lodash = _.runInContext();
function mapFilter(ary, iterator) {
if (!!this.__currentChain) {
return this.__currentChain.map(iterator).filter(item=>!!item);
}
return lodash.chain(ary)
.map(iterator)
.filter(item=>!!item)
.value()
}
lodash.mixin(lodash, mapFilter, {chain:true});
Obviously, the above is made-up, but hopefully, it makes it clear what I am trying to achieve. I could of course, just not have my function and do a map() then a filter() but since I am doing it a lot, I'd like to have less typing. Also, the example could be longer, doing much more but still wanting to tap into the current chain.
Is this possible? That is my question. Obviously, I can think of a million and one alternative solutions but I am fine with those. Just looking for a lodash expert to say, "no not possible",or "yes, you do this".
Upvotes: 1
Views: 1517
Reputation: 6703
One of the ways to check if a function is called in a chain is to check whether this
is LodashWrapper
object or not. Then, use the first argument as an iterator when it's in a chain.
const _ = require('lodash');
const lodash = _.runInContext();
function mapFilter(array, iterator) {
if (this.constructor.name === 'LodashWrapper') {
return this.map(array).filter(item => !!item);
}
else {
return lodash.chain(array).map(iterator).filter(item => !!item).value();
}
}
lodash.mixin({ mapFilter }, { chain: true });
const filter = x => x == 2 ? [x] : null;
console.log(lodash.mapFilter([1, 2, 3], filter));
console.log(lodash.chain([1, 2, 3]).mapFilter(filter).head().value());
console.log(lodash([1, 2, 3]).mapFilter(filter).head());
By the way, when you use explicit _.chain
, lodash doesn't apply shortcut fusion as you might expect. So you may want to use an implicit chaining. See Explicit chaining with lodash doesn't apply shortcut fusion for details.
Upvotes: 1
Reputation: 18515
I posted this as a comment but I feel that is what you would want either as a drop in or as something you need to check the source of how it is done and then code your own method or take pieces from it as part of your mixin etc.
The lodash _.tap method is there with the purpose of tap into" a method chain sequence in order to modify intermediate results
so that you do not have to call value
etc. You can use this as a starting point.
Hope this helps.
Upvotes: 2