使用lodash:使用不同的对象数组过滤对象数组

17

这个问题是特定于 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


你想根据用户和年龄找到重复的内容? - Cory Danielson
@CoryDanielson - 尝试根据用户和年龄属性找到匹配项。我会称之为匹配,而不是重复。 - mg1075
1
有趣的是,你的原始解决方案迄今为止是最快的。我认为如果其他人有重复项,它可能会产生重复项,但是如果在执行result.push(n)后返回false;,那么应该没问题。 - Cory Danielson
@CoryDanielson - 很好了解;你用什么进行性能测试?其他答案可能更加语法糖化,但如果这个版本显著更快,我会继续使用这个路线。 - mg1075
1
https://jsperf.com/testingdiwq 我添加了更多的数据,但是如果你修剪others,你会开始看到你和我的差异很大。但这是微不足道的。无论解决方案如何,所有这些都发生得非常快。必须平衡性能与清晰代码。通常情况下,清晰的代码更好,因为大多数性能问题都与DOM相关。 - Cory Danielson
@CoryDanielson - 太棒了。如果你想把这个加到你的答案里,我会接受的。 - mg1075
5个回答

11

你可以对其他内容进行索引,然后获取所需的结果,而无需嵌套循环。无论数据量大小,这应该是一个相对高效的解决方案:

// 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交互是前端的主要性能瓶颈。如果你对大数据集运行它并注意到锁定,你肯定会想进一步优化它,使用for循环而不是使用lodash函数进行迭代...但你通常不会遇到JavaScript中那种类型的数据... SQL和其他语言会更好地处理它。


嗯,将查找字符串创建为一种“魔术字符串”会导致意外问题吗?举个例子,假设用户是整数,您有用户1和用户11,而用户1的年龄是11岁,用户11的年龄是1岁?显然,这在当前的数据结构中是不现实的,但在这种格式下,这种类型的不匹配可能存在吗? - mg1075
是的,在那些情况下肯定会发生。你需要做一棵树或其他安全的数据结构来避免这种问题。虽然性能提升微不足道,但如果这是在非常大的数据集上执行,那么你需要切换到for循环来获得良好的性能。 - Cory Danielson
在您的魔术字符串中,您也可以使用一个分隔符来分隔用户和年龄值,例如"user11-11",其中"-"分隔用户和年龄,以避免那种问题。 - Cory Danielson
1
indexBy 似乎已被重命名为 keyBy。 - sajid sharlemin
1
请注意,如果您可以在代码开头初始化并保留查找数组,使用查找方法可能实际上会更快。jsperf 只显示 1% 的差异较慢,但这很可能是因为它必须每次都创建查找数组。对于该测试用例,它实际上要做两倍的工作。 - codenamezero

10

我想到了一个更简洁的方法:

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

编辑: 抱歉,JS Bin 的输出混淆了嵌套的数组。


语法很简洁明了,但实际上并没有返回所期望的结果。所期望的结果是一个对象数组,而不是一个数组嵌套的数组。这里返回的是一个数组嵌套的数组。 - mg1075
谢谢,现在返回对象。 - mg1075

5
使用ES6箭头函数和lodash的reject方法:
const result = _.reject(users, (item) => _.find(others, { user: item.user }));

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

0
如果您正在使用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;
    })

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接