Reputation: 685
I implemented a mixin to add "or" condition with _.where
var arr = [{a:1,b:4}, {a:5}, {a:6}, {a:11}];
_.mixin({
or: function(obj,arr,condition){
return _.chain(arr).where(condition).union(obj).value();
}
});
now i can use it like this and it works perfectly somewhat like a sql query
_.chain(arr).where({a:1}).or(arr,{a:11,b:3}).or(arr,{a:2}).value();
//returns [{a:1,b:4}]
_.chain(arr).where({a:1}).or(arr,{a:11}).or(arr,{a:2}).value();
//returns [{a:1,b:4},{a:11}]
_.chain(arr).where({a:1}).or(arr,{a:11}).or(arr,{b:4}).value();
//returns [{"a":1,"b":4},{"a":11}] --no duplicates
but I want to find a better way to just call _.or({a:1}), right now I have to pass arr everytime _.or(arr,{a:1}), as chained "or" gets first object as the result of previously executed functions.
Is there any way to get entire array in chained mixin function?
I want below to return the same result as my above implementation. (like a perfect sql query)
_.chain(arr).where({a:1}).or({a:11,b:3}).or({a:2}).value();//returns [{a:1,b:4}]
my prime focus is to get it through underscore, but really any other way like lodash or even some other library or solution will work too. Also it's not required that we use chaining, i am trying with compose as well but so far no luck. I want a solution which work perfectly in minimum lines. Any other suggestion to make it better is encouraged.
Upvotes: 4
Views: 2009
Reputation: 7624
I don't think extending underscore is the right way, Underscore's chaining is not desinged for this. You may "bend" it, but at cost of your desired shortness.
Underscore's chaining is a "stream operation". chain()
stores the passed object internally in a closure and returns the Underscore interface where each method is bound to that object. When a filter function is applied, the result is returned and internally stored in a result object. The value()
call will then return the internal reference.
You can solve this using a so called fluent interface:
// Fluent interface to filter an array with chained, alternative conditions.
// Usage: whereOr([...]).or({...}).or({...}).end()
// whereOr([...], {...}).or({...}).end()
// Objects are _.where() conditions to subsequently apply to the array.
function whereOr(arr, condition) {
var result = [],
iface;
iface = {
or: function(subcondition) {
result = result.concat(_.where(arr, subcondition));
return iface;
},
end: function() {
return _.union(result);
}
};
if (condition) {
return iface.or(condition);
}
return iface;
}
You can then do
var arr = [{a:1,b:4}, {a:5}, {a:6}, {a:11}];
whereOr(arr).or({a:1}).or({a:11}).or({a:2}).end();
If you like, you could mix this function into Underscore, of course:
_.mixin({
whereOr: whereOr
});
When whereOr()
is called it establishes a scope that holds the reference to the original arr
. It returns an object that provides the fluent functions or()
and end()
which both have access to the inital array. While end()
will return the result, or()
will apply Userscore's where()
to the internal reference and add the result to result
. It then returns the interface object again. This way or()
provides another possibility to filter until end()
returns the unionized result. In this example whereOr()
optionally accepts a first filter condition as second argument.
You may compare whereOr()
with chain()
and end()
with value()
.
Upvotes: 1
Reputation: 1975
Try this:
// makeOr is a constructor for a 'chainable' type with: `{or, value}`
var makeOr = () => {
var fs = []
var obj = {
or: f => {
fs.push(f)
return obj
},
value: (arr) => _.chain(fs).map(f => _.where(arr, f)).union().flatten().value()
}
return obj
}
// override underscore where
// if f is our 'chainable' type we evaluate it, otherwise we default to underscore where
(function() {
where = _.where
_.mixin({
where: (arr, f) => (!!f.value && !!f.or) ? f.value(arr) : where(arr, f)
})
})();
var result = _.chain(arr)
.where(makeOr().or({a: 1}).or({a:5}).or({a:11}))
.where({a:5}).value()
PS. Check this out too: Hey Underscore, You're Doing It Wrong!
Upvotes: 1
Reputation: 146
This is an interesting problem to take on, thank you for bringing this to my attention Rahul!
I came up with a function that solves this problem using underscore helper functions (though without chaining). It takes 2 arguments, the array
you start with, and the propsObj
array with properties you want to match.
Upvotes: 2