mg1075
mg1075

Reputation: 18155

lodash: filter array of objects with a different array of objects

This question is specific to lodash.

Given two arrays of objects, what is the best way to filter one array with the objects of the other array? I have attempted to put forth a scenario below, and the way I have gone about doing this is using two .forEach loops, but I would like to know if using lodash there is a better way to go about this type of filtering.


Example
The main source array of objects is users.

var users = [
  { 'user': 'barney', 'age': 36, 'active': true },
  { 'user': 'joe', 'age': 40, 'active': false },
  { 'user': 'fred', 'age': 50, 'active': false },
  { 'user': 'fred', 'age': 60, 'active': false },
  { 'user': 'fred', 'age': 70, 'active': false },
  { 'user': 'fred', 'age': 22, 'active': false },
  { 'user': 'fred', 'age': 25, 'active': false },
  { 'user': 'barney', 'age': 40, 'active': false },
  { 'user': 'pebbles', 'age': 1,  'active': true }
];

The array of objects that will filter the users array is called others.

var others = [
  { 'user': 'fred', 'age': 60 },
  { 'user': 'fred', 'age': 70},
  { 'user': 'fred', 'age': 22}
];

The desired result based on others filtering users is:

[
  { 'user': 'fred', 'age': 60, 'active': false },
  { 'user': 'fred', 'age': 70, 'active': false },
  { 'user': 'fred', 'age': 22, 'active': false }
];

Here is one way to obtain the desired result.

var result = [];

_.forEach(users, function (n, key) {
   _.forEach(others, function (n2, key2) {
      if (n.user === n2.user && n.age === n2.age) {
         result.push(n);
      }
   });
});

console.log(result);

Here is the example on jsbin.
http://jsbin.com/hapariviya/1/edit?html,js,console,output

Upvotes: 17

Views: 48087

Answers (5)

Andrien Pecson
Andrien Pecson

Reputation: 282

If you are using lodash and ES6 syntax.

    const users = [
      { 'user': 'barney', 'age': 36, 'active': true },
      { 'user': 'joe', 'age': 40, 'active': false },
      { 'user': 'fred', 'age': 50, 'active': false },
      { 'user': 'fred', 'age': 60, 'active': false },
      { 'user': 'fred', 'age': 70, 'active': false },
      { 'user': 'fred', 'age': 22, 'active': false },
      { 'user': 'fred', 'age': 25, 'active': false },
      { 'user': 'barney', 'age': 40, 'active': false },
      { 'user': 'pebbles', 'age': 1,  'active': true }
    ];

    const filters = [
      { 'user': 'fred', 'age': 60, 'active': false },
      { 'user': 'fred', 'age': 70, 'active': false },
      { 'user': 'fred', 'age': 22, 'active': false }
    ];


    _.filter(users, ({user, age, active}) => {
        return _.findIndex(filters, ({user:filterUser, age:filterAge, active:filterActive}) => { return (user == filterUser && age == filterAge && active == filterActive) }) >= 0;
    })

Upvotes: 0

Cory Danielson
Cory Danielson

Reputation: 14501

You can index the others, and then get the desired results without having to nest loops. It should be a relatively efficient solution, regardless of the amount of data:

// index others by "user + age"
var lookup = _.keyBy(others, function(o) { return o.user + o.age.toString() });
// find all users where "user + age" exists in index, one loop, quick lookup. no nested loops
var result = _.filter(users, function(u) {
    return lookup[u.user + u.age.toString()] !== undefined;
});

This gives the same result:

[
  { 'user': 'fred', 'age': 60, 'active': false },
  { 'user': 'fred', 'age': 70, 'active': false },
  { 'user': 'fred', 'age': 22, 'active': false }
];

Interestingly, your original solution was the most performant of all of these answers.

http://jsperf.com/testingdiwq

The performance concerns are pretty negligible here. In most cases, the DOM interaction is the main performance bottleneck of the front-end. If you were to run this against huge datasets and noticed the locking, you'd definitely want to optimize it further by using for loops instead of iterating with lodash functions.... but you won't typically come across that kind of data in JavaScript... SQL and others would handle it better.

Upvotes: 11

Georgian-Sorin Maxim
Georgian-Sorin Maxim

Reputation: 224

Using ES6 fat arrows and lodash's reject:

const result = _.reject(users, (item) => _.find(others, { user: item.user }));

Upvotes: 5

Chavdar Slavov
Chavdar Slavov

Reputation: 875

Here is cleaner way i can think of:

var result = _.flatten(_.map(others, function(item){
  return _.filter(users, item);
}));

Edit: Apologies JS Bin output was obfuscating the nested array.

Upvotes: 10

Christian
Christian

Reputation: 7862

var result = _.flatten(_.map(others, function(other){return _.where(users, other);}));

Upvotes: 2

Related Questions