【问题标题】:lodash Filter collection using array of valueslodash 使用值数组过滤集合
【发布时间】:2013-06-19 13:26:44
【问题描述】:

我想使用属性值数组过滤集合。给定一个 ID 数组,返回具有匹配 ID 的对象。有没有使用lodash/underscore的捷径方法?

var collections = [{ id: 1, name: 'xyz' },
                   { id: 2,  name: 'ds' },
                   { id: 3,  name: 'rtrt' },
                   { id: 4,  name: 'nhf' },
                   { id: 5,  name: 'qwe' }];
var ids = [1,3,4];

// This works, but any better way?

var filtered = _.select(collections, function(c){    
    return ids.indexOf(c.id) != -1
});

【问题讨论】:

  • 并非如此。但是你可以直接处理数组原型的filter() 方法;看起来更干净:)

标签: underscore.js lodash


【解决方案1】:

使用 indexBy()at() 的简洁 lodash 解决方案。

// loDash 4
_.chain(collections)
 .keyBy('id')
 .at(ids)
 .value();

// below loDash 4
_(collections)
 .indexBy('id')
 .at(ids)
 .value();

【讨论】:

  • 这个解决方案的简洁性太棒了。 Lodash 4 用户只需将 indexBy 替换为 keyBy 即可继续运行。
  • 如果collections 不包含任何与ids 匹配的对象,它似乎返回一个包含一个未定义元素的数组。 [undefined]。这没有通过我对someArray.length 的测试,所以我添加了一个.filter() _(collections).keyBy('id').at(ids).filter().value();
【解决方案2】:

这对我很有用:

let output = _.filter(collections, (v) => _.includes(ids, v.id));

【讨论】:

  • 谢谢,这对我也有用,但是,我使用reject 来简化过滤器let output = _.reject(collections, v => _.includes(ids, v.id));
  • @edencorbin 如果 id 也是同时具有 id 和 name 属性的对象列表,如何实现
  • 可能有更少的代码方法,但我会将其减少到只有 id,例如 const ids = arrayofobjects.map(x=>x.id)
【解决方案3】:

这些答案对我不起作用,因为我想过滤非唯一值。如果你把keyBy改成groupBy就可以了。

_(collections)
  .groupBy(attribute)
  .pick(possibleValues)
  .values()
  .flatten()
  .value();

我最初的用途是丢东西,所以我把pick换成了omit

感谢Adam Boduch 的起点。

【讨论】:

    【解决方案4】:

    我注意到其中许多答案已经过时或包含 Lodash 文档中未列出的辅助功能。接受的答案包括不推荐使用的函数_.contains,应该更新。

    这是我的 ES6 答案。

    基于Lodash v4.17.4

    _.mixin( {
        filterByValues: ( c, k, v ) => _.filter(
            c, o => _.indexOf( v, o[ k ] ) !== -1
        )
    } );
    

    并这样调用:

    _.filterByValues(
        [
            {
                name: 'StackOverflow'
            },
            {
                name: 'ServerFault'
            },
            {
                name: 'AskDifferent'
            }
        ],
        'name',
        [ 'StackOverflow', 'ServerFault' ]
    );
    
    // => [ { name: 'StackOverflow' }, { name: 'ServerFault' } ]
    

    【讨论】:

      【解决方案5】:

      如果您要经常使用这种模式,您可以创建一个如下所示的 mixin,但它与您的原始代码并没有什么根本不同。它只是让它对开发人员更加友好。

      _.mixin({
        'findByValues': function(collection, property, values) {
          return _.filter(collection, function(item) {
            return _.contains(values, item[property]);
          });
        }
      });
      

      那你就可以这样使用了。

      var collections = [
          {id: 1, name: 'xyz'}, 
          {id: 2,  name: 'ds'},
          {id: 3,  name: 'rtrt'},
          {id: 4,  name: 'nhf'},
          {id: 5,  name: 'qwe'}
      ];
      
      var filtered = _.findByValues(collections, "id", [1,3,4]);
      

      更新 - 上面的答案陈旧而笨拙。请使用answer from Adam Boduch 获得更优雅的解决方案。

      _(collections)
        .keyBy('id') // or .indexBy() if using lodash 3.x
        .at(ids)
        .value();
      

      【讨论】:

      • 您缺少 keyBy 的结尾引号。我会更正,但不允许我添加单个字符;)
      【解决方案6】:

      我们也可以这样过滤

      var collections = [{ id: 1, name: 'xyz' },
                  { id: 2,  name: 'ds' },
                  { id: 3,  name: 'rtrt' },
                  { id: 4,  name: 'nhf' },
                  { id: 5,  name: 'qwe' }];
      
      
      
              var filtered_ids = _.filter(collections, function(p){
                  return _.includes([1,3,4], p.id);
              });
      
              console.log(filtered_ids);
      

      【讨论】:

        【解决方案7】:

        我喜欢jessegavin's 的答案,但我使用lodash-deep 对其进行了扩展以进行深度属性匹配。

        var posts = [{ term: { name: 'A', process: '123A' } }, 
                     { term: { name: 'B', process: '123B' } }, 
                     { term: { name: 'C', process: '123C' } }];
        
        var result = _.filterByValues(posts, 'term.process', ['123A', '123C']);
        // results in objects A and C to be returned
        

        jsFiddle

        _.mixin({
            'filterByValues': function(collection, key, values) {
                return _.filter(collection, function(o) {
                    return _.contains(values, resolveKey(o, key));
                });
            }
        });
        
        function resolveKey(obj, key) {
            return (typeof key == 'function') ? key(obj) : _.deepGet(obj, key);
        }
        

        如果您不信任 lodash-deep 或者您希望支持名称中带有点的属性,这里有一个更具防御性和健壮性的版本:

        function resolveKey(obj, key) {
            if (obj == null || key == null) {
                return undefined;
            }
            var resolved = undefined;
            if (typeof key == 'function') {
                resolved = key(obj);
            } else if (typeof key == 'string' ) {
                resolved = obj[key];
                if (resolved == null && key.indexOf(".") != -1) {
                    resolved = _.deepGet(obj, key);
                }
            }
            return resolved;
        }
        

        【讨论】:

        • lodash get 默认是深的,所以你可以做类似_.get(object, 'a[0].b.c'); 的事情。因此,要使@jessegavin 回答支持深层属性,您所要做的就是将item[property] 更改为_.get(item, property)。见lodash documents
        猜你喜欢
        • 1970-01-01
        • 2020-08-27
        • 2019-01-22
        • 2023-03-10
        • 2016-02-25
        • 2021-02-21
        • 1970-01-01
        • 2017-09-01
        • 1970-01-01
        相关资源
        最近更新 更多