lodash按属性过滤嵌套数组

4
我有一个用户数组,其中有一个属性数组'rights',我想过滤掉具有特定权限的用户。我想通过一个数组进行过滤,所以如果我想要所有拥有完全权限['full']或同时具有完全和编辑权限['full','edit']的用户。我对使用lodash比较新,我认为我可以把它们链接在一起,但我不确定是否有更有效的方法。
这是我的plunker:http://plnkr.co/edit/5PCvaDJaXF4uxRowVBlK?p=preview 结果 ['full']:
[{
    "name": "Company1 Admin",
    "rights": [
      "full"
    ]
  },
  {
    "name": "FullRights Company1",
    "rights": [
      "full","review"
    ]
  }]

结果 ['完整','编辑']:

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

代码:

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>

3个回答

3

来自文档

_.filter(collection, predicate, thisArg);

参数

  • collection(数组|对象|字符串):要迭代的集合。

  • [predicate = _.identity](函数|对象|字符串):每次迭代调用的函数。

  • [thisArg](*):predicate的this绑定。


当您想要连接不同的处理步骤时,使用链接非常方便。

如果您的问题陈述是:

  1. 按权限过滤
  2. 按最年长的人排序
  3. 取10个

那么使用链接会非常有意义。

此问题似乎主要涉及筛选的自定义逻辑。

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*/]

我觉得我的示例很好,因为它不依赖于条件逻辑。它使用了lodash定义的方法,如anycontains


性能问题

我想进一步讨论您所担心的性能问题。以下是几点:

  • 您的问题代码正在维护自己的机制以过滤用户。虽然这是一个完全可行的解决方案,但您应该选择让维护lodash的人处理此逻辑。他们可能已经花费了大量的时间来优化如何从原始数组创建另一个数组。

  • _.any_.intersection更有效率。_.intersection需要处理每个元素才能知道交集是什么。_.any在命中第一个通过谓词的元素时停止,否则会检查其中的每一个。尽管有少量“rights”,但这一点是次要的。

  • 我给出的示例可能更符合“lodash标准”。您通常可以完全使用lodash定义的方法和“琐碎”的谓词进行数据转换。


感谢您的积极回应。您认为这种方法比使用forEach和intersection更快吗?我还没有进行任何JavaScript基准测试,但这将被频繁运行,速度是一个因素。 - Enkode
1
嘿,我很高兴能帮忙。从一个大O的角度来看,《_.foreach》等同于《_.filter》(它只是在列表上迭代执行操作)。《_.any》比《_.intersect》更高效,因为《_.intersect》必须处理所有的权限。我预期这个也会更快,因为它没有创建新数组的内存开销,而创建新数组可能非常昂贵。 - t3dodson
谢谢,如果可以的话,我想先获得更多反馈再确认您的答案是否正确。再次感谢您。 - Enkode

3

以下是对 @t3dodson 答案的更新。如果使用当前(4.17.4)版本的 Lodash,请使用以下代码片段:

function filterByRights (users, rights) {
  return _.filter(users, function (user) {
    return _.some(user.rights, function (right) {
      return _.includes(rights, right);
    });
  });
}

更新日志中:

删除了 _.contains 方法,推荐使用 _.includes

删除了 _.any 方法,推荐使用 _.some


1

我认为你在使用 intersection() 时走的是正确的道路(我从未见过这个函数有任何性能问题)。以下是我使用 flow() 组合迭代器的方法:

_.filter(users, _.flow(
    _.property('rights'), 
    _.partial(_.intersection, filterRights), 
    _.size
));

property()函数获取rights属性,并将其传递给intersection()。我们已经部分应用了filterRights数组。最后,size()函数是必要的,以向filter()传递一个真/假值。


谢谢Adam,我会审核这个并可能在几种不同的方法上运行一些压力测试。 - Enkode

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