Lodash: фильтр массива объектов с другим массивом объектов

Этот вопрос относится к lodash.

Учитывая два массива объектов, каков лучший способ для фильтрации одного массива с объектами другого массива? Я попытался привести сценарий ниже, и способ, которым я занимался этим, - использовать два цикла .forEach, но я хотел бы знать, можно ли использовать lodash лучше для этого типа фильтрации.


Пример
Основной исходный массив объектов - 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 }
];

Массив объектов, которые будут фильтровать массив users, называется others.

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

Желаемый результат, основанный на фильтрации others users:

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

Вот один из способов получить желаемый результат.

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);

Вот пример на jsbin.
http://jsbin.com/hapariviya/1/edit?html,js,console,output

Ответ 1

Вы можете индексировать остальные, а затем получать желаемые результаты без необходимости устанавливать петли. Это должно быть относительно эффективным решением, независимо от объема данных:

// 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;
});

Это дает тот же результат:

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

Интересно, что ваше оригинальное решение было наиболее эффективным из всех этих ответов.

http://jsperf.com/testingdiwq

Проблемы с производительностью здесь довольно незначительны. В большинстве случаев взаимодействие с DOM является основным узким местом производительности переднего плана. Если бы вы запускали это против огромных наборов данных и заметили блокировку, вы определенно захотите оптимизировать ее дальше, используя для циклов вместо повторения с помощью lodash-функций.... но вы обычно не сталкиваетесь с такими данными в JavaScript... SQL и другие справятся с этим лучше.

Ответ 2

Вот более чистый способ, о котором я могу думать:

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

Изменить: Извинения вывода JS Bin были обфускацией вложенного массива.

Ответ 3

Использование стрелок жира ES6 и lodash reject:

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

Ответ 4

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

Ответ 5

Если вы используете синтаксис lodash и ES6.

    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;
    })