获取过滤值的对象键

28

情况很简单 - 我有一个以下的对象:

Object {1: false, 2: true, 3: false, 4: false, 5: false, 6: false, 7: false, 8: true, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false} 

我需要使用underscore获取所有值为true的id数组。在上述情况下,代码如下:

[2, 8]

我尝试了一些方法,但是卡住了。有人有什么想法吗?

14个回答

43
var keys = [];
_.each( obj, function( val, key ) {
  if ( val ) {
    keys.push(key);
  }
});

使用普通的Underscore可能有更简单/更短的方法。


如果这里有人使用Lodash而不是Underscore,则也可以使用以下方法,它非常易于阅读

var keys = _.invert(obj, true)[ "true" ];

5
纯JavaScript(不含下划线)代码长度相同。我原本希望在文档中能找到我所遗漏的方法... - ducin
是的,可能没有更短的方法。 - gustavohenke
Underscore 中没有提供这样的方法,但您可以通过使用 _.extend 轻松提供一个。请查看我的回应:您可以查找第一个或所有键,并使用函数作为条件。 - gentiane

9

Giorgi Kandelaki提供了一个不错的答案,但是可能存在一些潜在问题(请参见我在他回答中的评论)。

正确的方法是:

_(obj).pairs().filter(_.last).map(_.first)

或者

_.map(_.filter(_.pairs(obj),_.last),_.first)

这个解决方案对我很有效。确切地说,第二行代码。只需用您选择的迭代器函数替换 _.last,就可以得到一个等效于尚不存在的下划线方法 _.findAllKeys 的语句! - Vikram Deshmukh

8
var data = {1: false, 2: true, 3: false, 4: true};

var filteredIds = _.filter(_.keys(data), function (key) {
    return data[key];
});

// result [2, 4]


var rejectedIds = _.reject(_.keys(data), function (key) {
    return data[key];
});

// result [1, 3]

哦,这个不错! - ducin

6
您可以使用_.pick。像这样:
var data = {1: false, 2: true, 3: false, 4: false, 5: false, 6: false, 7: false, 8: true, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false}

var keys = _.keys(_.pick(data, function(value) {
  return value;
}));

1
这个解决方案不起作用。将数据更改为[{a: true}, {b: false}, {c: true}, {d: false}],您将得到["0", "1", "2", "3"] - Christopher Harris

2
var obj = {1: false, 2: true /*...*/};

您可以使用reduce:

_(obj).reduce(function(memo, val, key){
    if (val)
        memo.push(key);
    return memo;
}, []);

或者链映射:
_(obj).chain().map(function(val, key){
    if (val)
        return key;
}).reject(_.isUndefined).value();

2
您可以尝试这个方法:
_.pairs(obj)
 .filter(function(pair) { return pair[1] })
 .map(function(pair) { return pair[0] })

或者更加简洁的方式:

_.pairs(obj).filter(_.last).map(_.first)

2
你的代码存在一些问题: pairs() 和 filter() 都会返回一个数组,而不是 underscore 对象,所以当在这个结果上调用 filter() 或 map() 时,你实际上是在原生数组对象上调用它们。这在现代浏览器中运行良好,但如果浏览器不支持 Array.filter 或 Array.map,则会失败。请参考我的答案,了解正确的处理方法。 - Cláudio Silva
哦,好发现。谢谢。我只是快速回答了一下,没有考虑浏览器兼容性。 - Giorgi Kandelaki

2
你可以很容易地做到这一点。
var obj = { a: false, b: false, c: true, d: false, e: true, f: false };
    name, names;

name = _.findKey(obj, true);
// -> 'c'
names = _.findKeys(obj, true);
// -> ['c', 'e']

你只需要稍微扩展一下underscore库即可实现这个功能:
_.mixin({

  findKey: function(obj, search, context) {
    var result,
        isFunction = _.isFunction(search);

    _.any(obj, function (value, key) {
      var match = isFunction ? search.call(context, value, key, obj) : (value === search);
      if (match) {
        result = key;
        return true;
      }
    });

    return result;
  },

  findKeys: function(obj, search, context) {
    var result = [],
        isFunction = _.isFunction(search);

    _.each(obj, function (value, key) {
      var match = isFunction ? search.call(context, value, key, obj) : (value === search);
      if (match) {
        result.push(key);
      }
    });

    return result;
  }
});

您甚至可以将函数用作过滤器,如下所示:

var team = {
  place1: { name: 'john', age: 15 },
  place2: { name: 'tim', age: 21 },
  place3: { name: 'jamie', age: 31 },
  place4: { name: 'dave', age: 17 }}

// Determine the places of players who are major
var placeNames = _.findKeys(team, function(value) { return value.age >= 18; });
// -> ['place2', 'place3']

祝你愉快 ;-)


1
这个过于复杂了。 - Justin Johnson
什么是复杂的?太长了,需要写成这样吗: name = _.findKey(obj, true); - gentiane
调用函数并不复杂。将 underscore 扩展以执行它已经能够执行的操作似乎对我来说是一种复杂化。 - Justin Johnson

1
这可能会更容易。
result = _.chain(obj)
    .map(function(value, key){
        return value?key:false;
    })
    .filter(function(v){
        return v
    })
    .value();

1
我的版本:
var list = {
    1: false, 2: true, 3: false, 4: false,
    5: false, 6: false, 7: false, 8: true,
    12: false, 13: false, 14: false, 15: false,
    16: false, 17: false, 18: false, 19: false
};

_.chain(list).map(function(val, key) {
    return val ? parseInt(key) : undefined
}).reject(function(val) {
    return _.isUndefined(val);
}).value();
// returns [2,8]

0

另一个可能的解决方案:

// Your example data
var data = {1: false, 2: true, 3: false, 4: false, 5: false, 6: false, 7: false, 8: true, 12: false, 13: false, 14: false, 15: false, 16: false, 17: false, 18: false, 19: false};

// Outputs the keys: ["2", "8"]
_.keys(_.transform(data, function(r, v, k) { v ? r[k] = 1 : null; }));

// Outputs the keys as integers: [2, 8]
_.map(_.keys(_.transform(data, function(r, v, k) { v ? r[k] = 1 : null; })), _.parseInt)

基本上:

  1. 将原始对象转换以剥离出值为false的键
  2. 从转换后的结果中获取键列表
  3. 如果需要,将键转换为整数

注意,transform 函数在 lodash 中存在,但在 underscore 中不存在。 - Pierre-Adrien

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