Reputation: 11356
Using lodash
_.find()
, we can see if there's a matching object in a collection.
var users = [
{ 'user': 'barney', 'age': 36, 'active': true, arr: [2, 3, 6, 9] },
{ 'user': 'fred', 'age': 40, 'active': false, arr: [2, 3, 6, 9] },
{ 'user': 'pebbles', 'age': 1, 'active': true, arr: [2, 3, 6, 9] }
];
You can provide an array, and an object matches if all values in that array are present in the collection. Notice the arr
.
_.result(_.find(users, { 'age': 1, 'active': true, 'arr': [2, 9] }), 'user');
// Result: "pebbles"
How can I match objects that have one (or more) matching array items for any array in the match object? (In this case arr
.) Something similar to the $in
query from MongoDB
.
_.result(_.find(users, { 'age': 1, 'active': true, 'arr': [2, 4] }), 'user');
// Result: undefined
// Expected: "pebbles"
Upvotes: 2
Views: 11874
Reputation: 434645
From the fine manual:
If an object is provided for
predicate
the created_.matches
style callback returnstrue
for elements that have the properties of the given object, elsefalse
.
and _.matches
does element-by-element equality tests with arrays so you can't use an object predicate
for what you want to do. However, you don't have to pass an object to _.find
, you can pass a function:
Iterates over elements of
collection
, returning the first elementpredicate
returns truthy for. The predicate is bound tothisArg
and invoked with three arguments: (value, index|key, collection).
The object form of _.find
that you're using just builds a predicate function behind the curtains. You can always supply your own predicate function if you need to:
_.find(users, function(value) {
if(value.age !== 1)
return false;
if(!value.active)
return false;
if(_.intersection(value.arr, [2, 4]).length <= 0)
return false;
return true;
})
Of course, if your arrays aren't really sets (i.e. there could be duplicates or order matters) then you'd have to replace with _.intersection
test with something that matches your specific requirements.
Demo: http://jsfiddle.net/ambiguous/vo72Lyce/
If you want this to work for arbitrary objects then you'd write your own version of _.matches
(probably by starting with the current implementation) that does what you want; then you could use your version of _.matches
to build the predicate function for _.find
.
Upvotes: 2
Reputation: 3067
As far as I know, you cannot do that in lodash.
What fuzzyMatcher
does is create an array of functions where each function corresponds to a single property in the object you are trying to match on. Those functions will return true if that property matches the property in the user object. Then fuzzyMatcher
runs each of those functions and gets the count of the functions which returned true. Then it returns true if the number of matching functions is greater than or equal the number of desired matches (1 in your case).
This seems to work pretty well:
function fuzzyMatcher(collection, matchObj, minSimilarProps) {
var matchFns = [];
_.forOwn(matchObj, function(value, key) {
matchFns.push(_.matchesProperty(key, value));
});
return _.find(collection, function(u) {
return _.filter(matchFns, function(fn) {
return fn(u);
}).length >= minSimilarProps;
});
}
//matches 1 properties
fuzzyMatcher(users, { 'age': 1, 'active': true, 'arr': [2, 4] }, 1);
//or matches 2 properties
fuzzyMatcher(users, { 'age': 1, 'active': true, 'arr': [2, 4] }, 2);
Or in your case:
_.result(fuzzyMatcher(users, { 'age': 1, 'active': true, 'arr': [2, 4] }, 2), 'user');
Also I think you need to reword what you want to say "two or more" since once or more would match barney since active=1 in both cases.
Upvotes: 0