Enkode Enkode - 4 years ago 732
Javascript Question

lodash filter by property array of array

I have array of users who have a property array 'rights' and I want to filter out the users who have specific rights. I would like to filter by an array so if I wanted all the users with full rights ['full'] or users with both full and edit ['full','edit']. I am fairly new to using lodash and I think I can chain some together but I am not sure if this is there are more efficient ways of doing it.

Here is my plunker: http://plnkr.co/edit/5PCvaDJaXF4uxRowVBlK?p=preview

Result ['full'] :

[{
"name": "Company1 Admin",
"rights": [
"full"
]
},
{
"name": "FullRights Company1",
"rights": [
"full","review"
]
}]


Result ['full','edit']:

[{
"name": "Company1 Admin",
"rights": [
"full"
]
},
{
"name": "FullRights Company1",
"rights": [
"full","review"
]
},
{
"name": "EditRights Company1",
"rights": [
"edit"
]
}]


Code:



var users = [
{
"name": "Company1 Admin",
"rights": [
"full"
]
},
{
"name": "FullRights Company1",
"rights": [
"full","review"
]
},
{
"name": "ApproveRights Company1",
"rights": [
"approve","review"
]
},
{
"name": "EditRights Company1",
"rights": [
"edit"
]
},
{
"name": "ReviewRights Company1",
"rights": [
"review"
]
},
{
"name": "NoRights Company1",
"rights": [
"none"
]
}
];

var tUsers = [];
var filterRights = ['full','edit'];
_.forEach(users, function(user) {
if (_.intersection(user.rights, filterRights).length > 0) {
tUsers.push(user);
}
}) ;

//console.log('users', JSON.stringify(users, null, 2));
console.log('tUsers', JSON.stringify(tUsers, null, 2));

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.1/lodash.min.js"></script>




Answer Source

From the docs

_.filter(collection, predicate, thisArg);

Arguments

  • collection (Array|Object|string): The collection to iterate over.

  • [predicate=_.identity] (Function|Object|string): The function invoked per iteration.

  • [thisArg] (*): The this binding of predicate.


Chaining is great when you want to connect different processing steps.

If your problem statement was to

  1. filter by rights
  2. sort by oldest person
  3. take 10

Then chaining would make a lot of sense.

This problem seems to be mostly custom logic on filtering.

var users = [/* Your user data here */];
function filterByRights (users, rights) {
    return _.filter(users, function (user) {
        return _.any(user.rights, function (right) {
            return _.contains(rights, right);
        });
    });
}
filterByRights(users, ['full', 'edit']); // [/*Users with full or edit rights*/]

I think my example is good becuase it doesn't depend on conditional logic. It uses lodash defined methods like any and contains


Performance concerns

I want to expand on what performance concerns you have. Here are a couple of points.

  • Your question code is maintaining its own mechanism for filtering out users. While it is a perfectly good solution you should opt into letting the guys who maintain lodash handle this logic. They have probably spent a lot of time optimizing how to create another array from an original one.

  • _.any is more efficient than _.intersection. _.intersection needs to process every element to know what the intersection is. _.any stops when it hits the first element which passes the predicate otherwise it checks each of them. This point is minor since there are a small number of "rights"

  • The example I've given is probably more "lodash standard". You typically can do data transformations completely with lodash defined methods and trivial predicates.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download