如何在JavaScript中查找另一个对象内包含特定属性的对象

5

我有一个包含所有用户的对象,如下所示:var users = {user1:{}, user2:{}},每个用户都有一个isPlaying属性。如何获取所有isPlaying为false的用户?


你需要遍历每个玩家并检查该属性。如果你使用的是像jQuery或underscore这样的库,你应该能够利用它们的一些辅助函数来完成这个任务... - Lix
@Lix 对于这个问题,使用原生JS比使用jQuery/Underscore更好。 - Matías Fidemraizer
@MatíasFidemraizer - 你是在谈论性能、可读性和方便性吗?这一切都取决于情况...虽然你的说法在大多数情况下可能是正确的,但它并不是适用于每一个 JS 使用情况的普遍规律... - Lix
@Lix 不需要。但这种情况是有道理的。为什么要使用库,当ECMA-Script 5.x已经足够了呢? - Matías Fidemraizer
4个回答

9

您应该使用 Object.keysArray.prototype.filterArray.prototype.map

// This will turn users object properties into a string array
// of user names
var userNames = Object.keys(users);

// #1 You need to filter which users aren't playing. So, you
// filter accessing users object by user name and you check that
// user.isPlaying is false
//
// #2 Using Array.prototype.map, you turn user names into user objects
// by projecting each user name into the user object!
var usersNotPlaying = userNames.filter(function(userName) { 
   return !users[userName].isPlaying; 
}).map(function(userName) {
   return users[userName];
});

如果使用ECMA-Script 6,您可以使用箭头函数来完成:
// Compact and nicer!
var usersNotPlaying = Object.keys(users)
                       .filter(userName => users[userName].isPlaying)
                       .map(userName => users[userName]);

使用 Array.prototype.reduce

正如 @RobG 所指出的,您也可以使用 Array.prototype.reduce

虽然我不想重复他的新答案,但我认为如果它返回一个未参加比赛的用户对象数组,那么reduce方法更实用。

基本上,如果您返回一个对象而不是一个数组,问题在于另一个调用者(即调用执行所谓的reduce的函数的函数)可能需要再次调用reduce来执行新的操作,而数组已经准备好流畅地调用其他 Array.prototype 函数,如 map filter forEach ...

代码将如下所示:

// #1 We turn user properties into an array of property names
// #2 Then we call "reduce" on the user property name array. Reduce
//    takes a callback that will be called for every array item and it receives
//    the array reference given as second parameter of "reduce" after 
//    the callback.
// #3 If the user is not playing, we add the user object to the resulting array
// #4 Finally, "reduce" returns the array that was passed as second argument
//    and contains user objects not playing ;)
var usersNotPlaying = Object.keys(users).reduce(function (result, userName) {
    if (!users[userName].isPlaying) 
        result.push(users[userName]);
    return result;
}, []); // <-- [] is the new array which will accumulate each user not playing

显然使用 Array.prototype.reducemapfilter 集中在单个循环中,在大型数组中,减少应该优于“filter+map”方法,因为对大型数组进行两次循环,一次过滤不玩游戏的用户,再次循环将它们映射回对象可能会很重...

总结:当涉及到少量项目时,我仍然会使用 filter+map 而不是 reduce,因为有时可读性/生产力比优化更重要,在我们的情况下,filter+map 方法需要的解释(自记录代码!)比 reduce 更少。

无论如何,可读性/生产力是由实际编码人员主观决定的...


不必使用 filter + map,你只需使用 reduceuserNames.reduce(function(names, name){if (!users[name].isPlaying) names.push(name); return names},[])。或者使用箭头函数:userNames.reduce((names,name)=>users[name].isPlaying?names:names.push(name)&&names,[])。但也许这开始变得有点晦涩了... - RobG
@robg 嗯,你说得对。现在我不在家,但我一旦能打开我的笔记本电脑就会更新我的答案 ;) - Matías Fidemraizer
@RobG 我本以为你是对的,顺便说一下,我认为在这种情况下 reduce 不起作用。我错了吗? - Matías Fidemraizer
“reduce” 是一个较少的方法调用。是否适合(也就是“有效”)取决于 OP 的想法吧。 - RobG
@RobG 最终你是对的。我也在我的答案中加入了你的想法,但我的意图并不是重复你的回答。事实上,我采用了返回数组而不是对象的想法。请看我的更新。 - Matías Fidemraizer
显示剩余2条评论

4

遍历您的用户对象:

var list = [];
for (var key in users) {
    if (users[key].isPlaying === false) {
        list.push(key);
    }
}

这将为您提供所有具有 isPlaying 属性为 false 的用户列表。

如果您想要所有 isPlaying 为 false 的用户对象,可以添加对象本身:

var list = [];
for (var key in users) {
    if (users[key].isPlaying === false) {
        list.push(users[key]);
    }
}

1
这也可以使用 Array.prototype.reduce 来实现,它是一个非常全面的工具。它从获取名称数组开始:
var userNames = Object.keys(users);

为了返回一个只包含 isPlaying 值为 false 的用户名称的数组,您可以执行以下操作:
var usersNotPlaying = userNames.reduce(function(names, name) {
                        if (!users[name].isPlaying) {
                          names.push(name);
                        }
                        return names}, []);

要返回一个以用户名作为键的用户对象的对象是相似的:

var usersNotPlaying = userNames.reduce(function(names, name) {
                        if (!users[name].isPlaying) {
                          names[name] = users[name];
                        }
                        return names}, {});

你也可以类似地使用forEach,不过由于它返回undefined,收集成员的对象或数组必须在外部作用域中初始化:
var usersNotPlaying = {};
userNames.forEach(function(name) {
  if (!users[name].isPlaying) {
    usersNotPlaying[name] = users[name];
  }
});

你也可以使用for..in
var usersNotPlaying = {};
for (var user in users) {
  if (users.hasOwnProperty(user) && !users[user].isPlaying) {
    usersNotPlaying[user] = users[user];
  }
}

以上所有内容都可以返回名称数组、用户对象数组或用户对象的对象。选择适合你的即可。;-)

好的,返回一个过滤后的对象听起来更好;D - Matías Fidemraizer

0
请尝试以下JS代码:将所有isPlaying设置为false。
var json_object={};//{"user1":{"isPlaying":"false"},"user2":{"isPlaying":"ture"}};
json_object['user1']={"isPlaying":"false"};
json_object['user2']={"isPlaying":"ture"};
console.log(json_object); 
for(var key in json_object){
    if(json_object[key].isPlaying === "false"){/*do what you want*/}
}
console.log(json_object);

在您的浏览器调试模式中查看console.log结果。 - Bruce Yo
1
我不理解你的解决方案! - Matías Fidemraizer
@Matías Fidemraizer 获取在/做你想做的事情/中isPlaying为false的用户,可以将其构建成其他数据格式。 - Bruce Yo
为什么你使用字符串而不是实际的布尔值 0_o - Matías Fidemraizer
抱歉,我的错误测试...布尔值肯定有效。谢谢~~ - Bruce Yo

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