5

I have a object with all my users, like so:var users = {user1:{}, user2:{}}, And every user has a isPlaying property. How do I get all users that have isPlaying false?

4
  • You'll have to iterate over each player and inspect that property. If you are using a library such as jQuery or underscore you should be able to utilize some of their helper functions for this... Commented Aug 2, 2015 at 7:03
  • @Lix Vanilla JS > jQuery/Underscore for this Commented Aug 2, 2015 at 7:12
  • @MatíasFidemraizer - are you talking about performance/readability/convenience? It all depends on the circumstances... While your statement would probably be true in most cases, it's not a general rule applicable to every instance of JS usage... Commented Aug 2, 2015 at 7:16
  • @Lix No. But this case makes sense. Why using a lib when ECMA-Script 5.x is enough. Commented Aug 2, 2015 at 7:23

4 Answers 4

9

You should use Object.keys, Array.prototype.filter and Array.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];
});

If it would be done using ECMA-Script 6, you could do using arrow functions:

// Compact and nicer!
var usersNotPlaying = Object.keys(users)
                       .filter(userName => users[userName].isPlaying)
                       .map(userName => users[userName]);

Using Array.prototype.reduce

As @RobG has pointed out, you can also use Array.prototype.reduce.

While I don't want to overlap his new and own answer, I believe that reduce approach is more practical if it returns an array of user objects not playing.

Basically, if you return an object instead of an array, the issue is that another caller (i.e. a function which calls the one doing the so-called reduce) may need to call reduce again to perform a new operation, while an array is already prepared to fluently call other Array.prototype functions like map, filter, forEach...

The code would look this way:

// #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

Clearly using Array.prototype.reduce concentrates both map and filter in a single loop and, in large array, reducing should outperform "filter+map" approach, because looping a large array twice once to filter users not playing and looping again to map them into objects again can be heavy...

Summary: I would still use filter+map over reduce when we talk about few items because sometimes readability/productivity is more important than optimization, and in our case, it seems like filter+map approach requires less explanations (self-documented code!) than reduce.

Anyway, readability/productivity is subjective to who does the actual coding...

Sign up to request clarification or add additional context in comments.

7 Comments

Rather then filter + map, you can just use reduce: userNames.reduce(function(names, name){if (!users[name].isPlaying) names.push(name); return names},[]). Or using an arrow function: userNames.reduce((names,name)=>users[name].isPlaying?names:names.push(name)&&names,[]). But maybe that's starting to get a bit obfuscated…
@robg uhm you're right. Now I'm not at home but I'll update my answer as soon as I can open my laptop ;)
@RobG I though you were right, BTW, I believe that reduce it doesn't work well in this scenario. Am I wrong?
reduce is one less method call. Whether it's appropriate (i.e. "works well") is up to the OP I guess.
@RobG It would be nice if you provide an answer using the reduce approach. I'm not sure that this approach is the right one to solve this concrete issue, since it feels "forced"... And your sample code in comments would return the array of user names rather than the object... Can you add an answer or a link to a jsFiddle or something like in order to fully understand how you want to achieve the same result as filter+map?
|
4

Iterate through your users object:

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

This will give you a list of all users who have an isPlaying property that is false.

If you would like all of the user objects where isPlaying is false, you can add the objects themselves instead:

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

Comments

1

This can also be achieved using Array.prototype.reduce, which is a great all round tool. It starts with getting an array of the names:

var userNames = Object.keys(users);

To return an array just the user names where isPlaying is false, you can do:

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

To return an object of user objects with their names as keys is similar:

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

You could also use forEach in a similar way, however since it returns undefined the object or array collecting the members must be initialised in an outer scope first:

var usersNotPlaying = {};
userNames.forEach(function(name) {
  if (!users[name].isPlaying) {
    usersNotPlaying[name] = users[name];
  }
});

You can also use for..in:

var usersNotPlaying = {};
for (var user in users) {
  if (users.hasOwnProperty(user) && !users[user].isPlaying) {
    usersNotPlaying[user] = users[user];
  }
}

All of the above can return an array of names, array of user objects or object of user objects. Choose whatever suits. ;-)

1 Comment

Ok, returning a filtered object sounds better ;D
0

Please try the JS code below: set all the isPlaying to 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);

5 Comments

See the console.log results in your browser's debug mode.
I don't understand your solution!
@Matías Fidemraizer get the users that have isPlaying false in /*do what you want*/, maybe constuct it into other data format.
And why you use strings instead of actual booleans 0_o
Sorry, my mistake for testing... booleans for sure works. thanks~~

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.