使用数组值过滤集合的lodash筛选器

63

我想使用属性值数组过滤集合。给定一个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() 方法来处理;看起来更简洁 :) - gustavohenke
8个回答

67

如果你要经常使用这种模式,你可以创建一个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]);

更新 - 上面的答案已经过时且笨重。请使用Adam Boduch的答案,这是一个更加优雅的解决方案。

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

1
你在keyBy上缺少结束引号。我会进行更正,但是SO不允许我添加一个字符 ;) - Damian Green

48

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

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

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

10
这个解决方案的简洁程度很棒。Lodash 4 的用户只需用 keyBy 替换 indexBy 即可继续使用此功能。 - vhs
4
如果 collections 中没有任何与 ids 相匹配的对象,它似乎会返回一个包含一个未定义元素的数组 [undefined]。这将导致我的 Go 测试失败,因此我添加了一个 .filter()_(collections).keyBy('id').at(ids).filter().value(); - steezeburger

19

我们也可以像这样进行过滤

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

11

这对我来说非常有效:

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

2
谢谢,这对我也有用,但是我使用了 reject 来简化过滤器
let output = _.reject(collections, v => _.includes(ids, v.id));
- Deano
@edencorbin 如果这些id也是一个对象列表,包含id和name属性,该如何实现呢? - indra257
可能有更少的代码方法,但我会将其简化为仅使用ID,例如:const ids = arrayofobjects.map(x=>x.id)。 - edencorbin

7

我喜欢jessegavin的答案,但我使用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;
}

3
默认情况下,lodash的get方法是深度获取的,因此您可以执行像这样的操作:_.get(object, 'a[0].b.c')。如果您想让 @jessegavin的答案支持深度属性,您只需要将item[property]替换为_.get(item, property)即可。请参见lodash文档 - danbars

3

这些答案对我无效,因为我想要根据非唯一值进行过滤。如果你将 keyBy 更改为 groupBy ,就可以实现。

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

我的初始使用是为了丢弃一些东西,所以我用omit替换了pick

感谢Adam Boduch提供的起点。


1
我注意到很多答案已经过时或包含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' } ]

0

很抱歉晚来参加派对,但现在最清晰的方法是使用_.reject

_.reject(collections, ({id}) => _.includes(ids, id));

基本上,我们正在拒绝满足特定条件的所有值,即ID包含在“ids”数组中的情况。

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