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".
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.
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.
Related
I have an iterator object, and I would like to get the first element that satisfies a testing function.
I know I can use Array.prototype.find if I convert the iterator to an array. using the [...ducks].find((duck) => duck.age >= 3) syntax for example.
But I do not want to convert the iterator to an array because it's wasteful. In my example, I have many ducks, and I need an old one that will be found after only a few tests.
I'm wondering whether an elegant solution exists.
There's nothing built in, but you can readily write a function for it, stick it in your utilities toolkit, and reuse it:
function find(it, callback) {
for (const value of it) {
if (callback(value)) {
return value;
}
}
}
Usage:
const duck = find(ducks, (duck) => duck.age >= 3);
Libraries like lodash implement a .chain() method in which each subsequent chained method will return a reference to this allowing for the chaining to be possible. The downside of which is that at the end of the chain, to resolve the set of functions the .value() method needs to be called to return the resulting value.
I am curious is there is away to know if the chain is terminated without the resolver function? Is there a way for each individual method to know if there is a subsequent call and if not return the resulting value?
Here's an example:
enum Direction {
LEFT = 'LEFT',
UP = 'UP',
DOWN = 'DOWN',
RIGHT = 'RIGHT',
}
class Move {
movement: Direction[];
constructor () {
this.movement = []
}
left () {
this.movement.push(Direction.LEFT)
return this
}
right () {
this.movement.push(Direction.RIGHT)
return this
}
up () {
this.movement.push(Direction.UP)
return this
}
down () {
this.movement.push(Direction.DOWN)
return this
}
resolve () {
return this.movement;
}
}
const m = new Move()
const code = m
.up()
.up()
.down()
.down()
.left()
.right()
.left()
.right()
.resolve();
console.log(code)
I am looking for a way to have the following and still have the same value as the m above, by somehow altering each method.
const code = m
.up()
.up()
.down()
.down()
.left()
.right()
.left()
.right();
Is there a way for each individual method to know if there is a subsequent call and if not return the resulting value?
No. You can use a timeout which is essentially polling and unreliable.
Real World
Needing a resolution method is not new e.g. Underscore uses the pair chain and value : https://underscorejs.org/#chaining
However, there is a pattern that allows this termination:
input.pipe(up(), up(), left());
By making the functions standalone and "piping" them, you know the chain ended inside pipe (note that these would have to be higher-order functions). This pattern is used by rxjs, for example, though there they don't care about termination of the chain at all. The reason it is done there (which is also nice) is that it gives you an easily extensible system of operators that can be tree-shaken.
Similarly you can get back to dot chaining (if you really want it) by just wrapping the chain:
input.chain(chain => chain.left().up());
The chain method can run the function provided to it and knows that at the end of this, the chain is terminated. So assuming your functions return an object with a value method, pipe/chain can just implicitly call this in the end:
chain(chainer: (obj: MyObj) => MyObj) {
return chainer(this).value();
}
Of course, essentially this is moving the burden of calling value in the end to calling the chain in the beginning.
One additional note, I would probably not implement the class to have this mutable state but, if you go with chaining, instead go the route of using an immutable object and each such operator returning a new object. This makes the chain behavior more predictable and sensible as you don't run the risk of applying a chain sequence twice etc.
Perhaps I missed something, but is there a better/more concise way of writing something like this:
var a=1,
b=2,
c=3;
if(a===1 && b===1 && c===1){ do something };
or
if(a===1 || b===1 || c===1){ do something };
I'm trying to keep the code small, so am not looking for iterating through arrays, etc like proposed in pages I've come across. I would think that there would be something (at least for the first example where they are all the same) that would look like
if(a=b=c===1){ do something? };
Thanks!
You can take a functional approach, and create a simple comparison function generator:
function eq(val) {
return function(x) {
return x === val
}
}
And then use .every for the &&:
if ([a,b,c].every(eq(1))) {
// all were equal
}
Or use .some for the ||:
if ([a,b,c].some(eq(1))) {
// at least one was equal
}
You could also create a function that receives the result of a condition and a function to invoke when the condition was true:
function when(cond, fn) {
if (cond)
fn.call.apply(fn, [].slice.call(arguments, 2))
}
And encapsulate your if body into a function...
function doIt(arg1, arg2) {
console.log("doing it!")
console.log(this, arg1, arg2)
}
Then use it like this:
when([a,b,c].every(eq(1)), doIt, null, "foo", "bar")
The third argument to when sets the this value of the callback, and subsequent arguments are passed as arguments to the callback.
No, there isn't. You have to check them individually. JavaScript just doesn't have an operator for what you're trying to do.
Your if(a=b=c===1){ ... (which I realize was just an example) would set a and b to be true (if c===1) or false (if it didn't) and then branch based only on c===1.
I mean, for that specific case, there are a couple of math approaches, but in the general case, no.
Depending on how much you want to invest, that would be a possible solution:
var slice=Function.call.bind([].slice)
var checkCondition=function(condition){
return function(){
return slice(arguments).reduce(function(o,n){
return o && o == condition(n);
},true);
}
}
greater5=checkCondition(function(x){ return x>5; });
console.log(greater5(4,7));
If you only have one or two times in your code where you have to check multiple variables it would be absolutely overkill. But if you have multiple places with varying arguments, it might help.
You could easily define new checks by definition of another function and add as many variables as you want.
Scenario: I'm searching for a specific object in a deep object. I'm using a recursive function that goes through the children and asks them if I'm searching for them or if I'm searching for their children or grandchildren and so on. When found, the found obj will be returned, else false. Basically this:
obj.find = function (match_id) {
if (this.id == match_id) return this;
for (var i = 0; i < this.length; i++) {
var result = this[i].find(match_id);
if (result !== false) return result;
};
return false;
}
i'm wondering, is there something simpler than this?:
var result = this[i].find(match_id);
if (result) return result;
It annoys me to store the result in a variable (on each level!), i just want to check if it's not false and return the result. I also considered the following, but dislike it even more for obvious reasons.
if (this[i].find(match_id)) return this[i].find(match_id);
Btw I'm also wondering, is this approach even "recursive"? it isn't really calling itself that much...
Thank you very much.
[edit]
There is another possibility by using another function check_find (which just returns only true if found) in the if statement. In some really complicated cases (e.g. where you don't just find the object, but also alter it) this might be the best approach. Or am I wrong? D:
Although the solution you have is probably "best" as far as search algorithms go, and I wouldn't necessarily suggest changing it (or I would change it to use a map instead of an algorithm), the question is interesting to me, especially relating to the functional properties of the JavaScript language, and I would like to provide some thoughts.
Method 1
The following should work without having to explicitly declare variables within a function, although they are used as function arguments instead. It's also quite succinct, although a little terse.
var map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return this.id == match_id ? this : map(this, function(u) {
return find.call(u, match_id);
}).filter(function(u) { return u; })[0];
};
How it works:
We test to see if this.id == match_id, if so, return this.
We use map (via Array.prototype.map) to convert this to an array of "found items", which are found using the recursive call to the find method. (Supposedly, one of these recursive calls will return our answer. The ones which don't result in an answer will return undefined.)
We filter the "found items" array so that any undefined results in the array are removed.
We return the first item in the array, and call it quits.
If there is no first item in the array, undefined will be returned.
Method 2
Another attempt to solve this problem could look like this:
var concat = Function.prototype.call.bind(Array.prototype.concat),
map = Function.prototype.call.bind(Array.prototype.map);
obj.find = function find(match_id) {
return (function buildObjArray(o) {
return concat([ o ], map(o, buildObjArray));
})(this).filter(function(u) { return u.id == match_id })[0];
};
How it works:
buildObjArray builds a single, big, 1-dimensional array containing obj and all of obj's children.
Then we filter based on the criteria that an object in the array must have an id of match_id.
We return the first match.
Both Method 1 and Method 2, while interesting, have the performance disadvantage that they will continue to search even after they've found a matching id. They don't realize they have what they need until the end of the search, and this is not very efficient.
Method 3
It is certainly possible to improve the efficiency, and now I think this one really gets close to what you were interested in.
var forEach = Function.prototype.call.bind(Array.prototype.forEach);
obj.find = function(match_id) {
try {
(function find(obj) {
if(obj.id == match_id) throw this;
forEach(obj, find);
})(obj);
} catch(found) {
return found;
}
};
How it works:
We wrap the whole find function in a try/catch block so that once an item is found, we can throw and stop execution.
We create an internal find function (IIFE) inside the try which we reference to make recursive calls.
If this.id == match_id, we throw this, stopping our search algorithm.
If it doesn't match, we recursively call find on each child.
If it did match, the throw is caught by our catch block, and the found object is returned.
Since this algorithm is able to stop execution once the object is found, it would be close in performance to yours, although it still has the overhead of the try/catch block (which on old browsers can be expensive) and forEach is slower than a typical for loop. Still these are very small performance losses.
Method 4
Finally, although this method does not fit the confines of your request, it is much, much better performance if possible in your application, and something to think about. We rely on a map of ids which maps to objects. It would look something like this:
// Declare a map object.
var map = { };
// ...
// Whenever you add a child to an object...
obj[0] = new MyObject();
// .. also store it in the map.
map[obj[0].id] = obj[0];
// ...
// Whenever you want to find the object with a specific id, refer to the map:
console.log(map[match_id]); // <- This is the "found" object.
This way, no find method is needed at all!
The performance gains in your application by using this method will be HUGE. Please seriously consider it, if at all possible.
However, be careful to remove the object from the map whenever you will no longer be referencing that object.
delete map[obj.id];
This is necessary to prevent memory leaks.
No there is no other clear way, storing the result in a variable isn't that much trouble, actually this is what variables are used for.
Yes, that approach is recursive:
you have the base case if (this.id==match_id) return this
you have the recursive step which call itself obj.find(match_id) { ... var result = this[i].find(match_id); }
I don't see any reason, why storing the variable would be bad. It's not a copy, but a reference, so it's efficient. Plus the temporary variable is the only way, that I can see right now (I may be wrong, though).
With that in mind, I don't think, that a method check_find would make very much sense (it's most probably basically the same implementation), so if you really need this check_find method, I'd implement it as
return this.find(match_id) !== false;
Whether the method is recursive is hard to say.
Basically, I'd say yes, as the implementations of 'find' are all the same for every object, so it's pretty much the same as
function find(obj, match_id) {
if (obj.id == match_id) return obj;
for (var i = 0; i < obj.length; ++i) {
var result = find(obj[i], match_id);
if (result !== false) return result;
}
}
which is definitely recursive (the function calls itself).
However, if you'd do
onesingleobjectinmydeepobject.find = function(x) { return this; }
I'm not quite sure, if you still would call this recursive.
EDIT:
I'm putting this at the top because ive finally figured out the actual problem.
Prototypejs is adding an Array.reduce function which is intefering with underscore (see: https://github.com/documentcloud/underscore/issues/7)
There doesn't seem to be anything conclusive here other than "use prototype > 1.6.1) but I can't control what prototype is used unfortunately. Other than altering the _.reduce method to not use the native function or proxying any method that uses reduce (see comments) I can't see any nice way of fixing this issue.
I'm having an issue with Prototypejs being included on the same page as my javascript "app" where I am using underscore.
It seems that whenever I try and use the function _.unique it is actually calling the prototype function instead, this is inside a closure and I am using requirejs to load in _. When I change the order of the libraries included so my app is included before prototype then everything works fine, unfortunately I cannot use this as a solution as I will have no control of how this is included in any page.
I was wondering if anyone had come across this problem before and had a possible solution where _.unique will always call the underscore function rather than any global prototype function called unique.
Thanks
EDIT:
I actually think I might be wrong about the unique method being overridden actually. I have just added some console logs into the underscore function and it seems it is being called but its returning empty:
_.uniq = _.unique = function(array, isSorted, iterator) {
console.log("called this");
console.log(array);
var initial = iterator ? _.map(array, iterator) : array;
var results = [];
// The `isSorted` flag is irrelevant if the array only contains two elements.
if (array.length < 3) isSorted = true;
_.reduce(initial, function (memo, value, index) {
console.log("it never gets here");
if (isSorted ? _.last(memo) !== value || !memo.length : !_.include(memo, value)) {
memo.push(value);
results.push(array[index]);
}
return memo;
}, []);
console.log(results);
return results;
};
The first console log gives me "[1,2,3,1]" whereas the second gives me "[]". This does only seem to happen when prototype is included on the page though, so something is going on with it.
Ive added another log (it never gets here) which is ever executed. It looks like underscore is executing the "native" reduce method which is the one provided by Prototypejs which doesnt take an iterator.
Yes, Prototype.js overrides reduce, that's really a bad idea. If reduce is the only thing that Prototype.js messed, so how about set Array.prototype.reduce to null just at the start of _.uniq? Seems that _.intersection and _.union both rely on _.uniq.
_.uniq = _.unique = function(array, isSorted, iterator) {
var keepIt = Array.prototype.reduce;
Array.prototype.reduce = null;
//....
Array.prototype.reduce = keepIt;
};