Iterate Through the Keys of an Object with a for...in Statement - unsure of code + the logic behind a for in loop

Tell us what’s happening:
hello all,

so i according to my thought process this code should work but it doesn’t… why?

my thought process: i initialize a variable called count to count the total number of people online
for (user in users) is to iterate across each user and user.online is to check each nested object in the big object users if they are online and increase the counter if they are online

then i return the count value which has the total number of users that are online.

also, if its not too much to ask, could anyone reexplain the for…in loop to me? i don’t understand how it works and how does the computer know to that user in for (let user in users) represents the user itself

what if there were more properties besides their name that are not nested? or what if the object wasnt a nested object (i.e. it was like name:alan, age:27, online:false, … )?

thank you for reading till here!!

Your code so far


let users = {
  Alan: {
    age: 27,
    online: false
  },
  Jeff: {
    age: 32,
    online: true
  },
  Sarah: {
    age: 48,
    online: false
  },
  Ryan: {
    age: 19,
    online: true
  }
};

function countOnline(obj) {
  // change code below this line
  let count = 0;
  for (let user in users){
    if (user.online){count++;}
  }
  // change code above this line
  return count;
}

console.log(countOnline(users));

Your browser information:

User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36.

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/basic-data-structures/-iterate-through-the-keys-of-an-object-with-a-for---in-statement

So, your issue is that you are not understanding what the for…in loop does - your approach is correct by you have an error that comes from that missing knowledge

My advice is add console.log(user) inside the loop
And read this

yeah i actl referred to that before posting this question on the forum…

i still dont get it could you provide more info/advice? :slight_smile: thanks!!

edit: i did the console log all i got was their names but when i console logged user.online i didnt get anything… not sure why?

for…in gives you the property keys of the object, which are strings, and if you do user.online you are trying to access the property online of a string, which is not possible
So how do you access object properties?

sorry i really dont understand your question… im really lost

all i know is this object has different people (is this a property no right?? i think each person is a variable…?), and each person has 2 properties (online and their age)

why cant i access them by using dot notation? is it the bracket notation where i dont put the quotes in? like [user]…?

actually i realized isnt this like a nested object with many objects(people) inside of it?

The object users has different properties with the key being a name and the value of the property being an other object

So, how do you get the value of the property Sarah?

Yes, it is a nested object

The issue is that you are trying to use the variable user as an object, but user is just a string - the issue in user.online is that user is a string and a string can’t have properties, because you are doing"Sarah".online

okay so i tried to internalize what you just said

so do i do obj[user].online as a way to access the online property?

also another question hopefully it doesnt get confusing:
for key-value pairs, the key is always a string?
(for user in obj) means that the user will always iterate over the keys which are all strings right?

You can also write an object with keys that are numerical, like for example

const THINGY = {
   1: "one";
   2: "two";
}

But using the for in loop, and other methods to extract keys you will still get them as string, in this case “1”, “2”

For this challenge, writing for (let user in users) you are using the global variable users so maybe you want to use the function parameter obj here too - but yes, that is the correct way to access the property value

okay so i tried to internalize what you just said

so do i do obj[user].online as a way to access the online property?

also another question hopefully it doesnt get confusing:
for key-value pairs, the key is always a string?
(for user in obj) means that the user will always iterate over the keys which are all strings right?

That should be correct.

Another option would be

  Object.keys(obj).forEach(item => {
    if(obj[item].online){count++}
  })

sorry i really dont understand your code could you explain it?

also is obj[user].online really the only way to access the online value? im new to all this and thus far im only used to using dot notation to solve problems… when do i know when to use bracket and when to use dot notation?

bc i remember one of the previous lessons mentioning either notation is fine. however here it appears to me you must use bracket notation?

thank you!!

so i guess the lesson here that i didnt know is whenever extracting keys you it will always be returned as a string?

and because its returned as a string i must use bracket notation? there really is no way to solve this using dot notation?

You have to forget about solving the problem for a second and understand this concept…

let obj = {
    name: 'xnmng',
    age: 21,
    programmer: true
};

Object stripped of values:
object = {
key: value,
key: value,
key: value
}

To get a value of key,. ie… your name… You wouldn’t say console.log(name);
You would do console.log(obj[‘name’]) or console.log(obj.name);

In your example there’s an extra layer. A wrapper object.
object = {
key1: { key2: value },
key1: { key2: value }
};

To access value you would need to do object['key1']['key2'] or (object.key1.key2)

But what you’re trying to do is key1['key2'] or (key1.key2)

let user in users…
user.online is the same as key1.online not object.key1.online

You have to forget about solving the problem for a second and understand this concept…

let obj = {
    name: 'xnmng',
    age: 21,
    programmer: true
};

Object stripped of values:
object = {
key: value,
key: value,
key: value
}

To get a value of key ,. ie… your name … You wouldn’t say console.log(name);
You would do console.log(obj[‘name’]) or console.log(obj.name);

In your example there’s an extra layer. A wrapper object.
object = {
key1: { key2: value },
key1: { key2: value }
};

To access value you would need to do object['key1']['key2'] or (object.key1.key2)

But what you’re trying to do is key1['key2'] or (key1.key2)

let user in users…
user.online is the same as key1.online not object.key1.online


okay yeah i get it then back to the problem, when i do obj[user].online i solve the challenge. however, if i do obj.user.online it doesn’t work…?

//this doesnt solve the challenge
function countOnline(obj) {
// change code below this line
let counter = 0;
for (let user in obj){
if (obj.user.online){
counter++;
}
}
return counter;
// change code above this line
}

//but this does
function countOnline(obj) {
// change code below this line
let counter = 0;
for (let user in obj){
if (obj[user]online){
counter++;
}
}
return counter;
// change code above this line
}

how do i make it such that i can access the value using dot notation?
(sorry if it seems like a dumb question )

So, to explain my code, Object.keys(‘yourObject’) will create an array of keys from the object. The keys will be strings.

check https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

the next part of it is the .forEach() This will do a for loop through each of the values in the array created by the Object.keys() method.So for each key, do something.

To break apart the elements of the forEach method read this ->https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach

I choose to use an arrow function, which is the es6 way of defining a function. On the left side of the arrow, item is the variable, which is actually the key value. On the right side of the arrow is the function. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

in the if statement, obj is the reference to the argument passed into your function. bracket notation is used for item because obj.item would return undefined since item is not a value in obj. We use bracket notation to specify a string or a variable(which evaluates to a string). obj.Alan.online is the same as obj[‘Alan’].online. Both of those are equal to the following as well.

let item=‘Alan’
obj[item].online

obj[item].online is going to evaluate to either true of false. This is the same as obj[item].online === false

You must use bracket notation because you use a variable, with the online key you use dot notation and that’s fine because you have a property key exactly like that. Instead to access the properties of the outer object you are using a variable, you don’t have a property key user but the variable user needs to be evaluated to the current value (“Sarah”, “Jeff”, etc) and for that you need bracket notation

1 Like

wow thank you very much that really helped!!

so moving forward if i wanted to use dot notation the whole object has to be changed to something like

let users = {
user: Alan {
age: 27,
online: false
},
user: Jeff {
age: 32,
online: true
},
user: Sarah {
age: 48,
online: false
},
user: Ryan {
age: 19,
online: true
}
};

would that then allow me to use users.user.online?

Nope, that is not correct syntax for N object - keys need to be unique if you want to access them all and this doesn’t follow the correct structure for an object

{
key: value;
key: value;
}

so actually theres nothing wrong (and by wrong i mean improvable) with their object declaration?

The object is fine, it has properties with a value of an other object

You just need to familiarise on when to use bracket or dot notation

Try this maybe:

thank you!

so i suppose the takeaway from this is that i cant use variables with dot notation (which was what i tried to do hence couldnt solve the challenge)

also because the for in loop iterates over each user (first “alan” then “jeff” etc.) each time it iterates will be equivalent to assigning their name to a variable (i.e. user) then using that to execute obj.[user].online