Trying to Get My Head Around for...in Statements

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

Hopefully anybody else struggling with this finds this useful :slight_smile:

This isn’t about passing the exercise! I’ve managed to do that contextually rather than specifically knowing really whats going on here. So I thought I’d set myself my own challenge to try and solidify my understanding of whats going on with for…in loops.

Rather than returning the count of online users, I thought to myself “how would I return an array of the ages of each user?” - The goal therefore is for ‘console.log(countOnline(users)’ to return [27, 32, 48, 19].

Here is how I did it!

function countOnline(obj) {
  // change code below this line
  let ages = [];                           //creates ages array to push age values in to
  for (let prop1 in obj) {                 //iterates through each key within the object
    for (let prop2 in obj[prop1]) {        //iterates through each key within the nested object of prop1
      if (prop2 == "age") {                //only push the value when prop2 is looking at age (not online)
        ages.push(obj[prop1][prop2]);      //push the value of the key to the array we made
      }
    }
  }
  return ages;                             //function returns the ages array 
  // change code above this line
}

Notice how (unexplained in the exercise) the variables we create, in my case ‘prop1’ and ‘prop2’, are just placeholders that essentially just take the form of “something that is changing on each iteration of the loop”.

You can name these variables whatever you like, but to me it seems to make sense that you’d refer to them as property/prop followed by the level of the property within the object that you’re changing for each iteration.

Because the second level of the users object is a nested object, we have to use the if statement to differentiate between ‘age’ and ‘online’. Whilst iterating through the loop, ‘prop2’ will take one of these values on one iteration, and the other value on the next iteration.

Hopefully someone finds this useful! Ninjas please do let me know if I haven’t explained anything correctly

2 Likes

Great that you have additional thoughts about this task!

I replicated the task this way:

function countOnline(obj) {
  let ages = [];

  for (let name in obj) {
    ages.push(obj[name]["age"]);
  }

  return ages;
}

I directly go into the age property,
because what would happen,
if every user would have 50 instead of 2 properties?
The algorithm would compare every of these 50 properties with “age”.

Additionally, we can include:
if (obj[name]["age"]){...}
to be sure to only include users with an age property.

Great job expanding on the lesson. This kind of thinking will speed up your learning in greater magnitudes.

Correct. Except semantically they are locally scoped variables.

Correct again. And you touched one of the 2 hardest things according to this classic joke

There are 2 hard problems in computer science: cache invalidation, naming things, and off-by-1 errors.

Variables should always have meaningful names. Ideally a person would be able to guess the purpose of the variable by it’s name. If you will have a short-lived scope, like in a loop, sometimes it’s easier to use a single letter. But that’s only really for tracking indexes.

This part right here is not really needed since you hard coded the string age.

if (prop2 == "age") {  
        ages.push(obj[prop1][prop2]); 
      }

But you’re thinking is right on track, because now you can abstract this function into a more generic and re-usable function like this.

function listOfUserProp(obj, prop) {
  // change code below this line
  let filtered = [];   
  for (let user in obj) { 
  // you can also reference the previous variable here
    for (let userProp in user) { 
      if (userProp == prop) {   
        ages.push(user[userProp]);
      }
    }
  }
  return filtered;                             //function returns the ages array 
  // change code above this line
}

Great work again.

Hi All!

Thanks for the feedback @miku86 & @JM-Mendez

@miku86 your solution is a lot nicer than mine! I think perhaps I got too bogged down in iterating through keys that I overlooked you can just pull these values out specifically, per iteration of the top level (user)

@JM-Mendez thanks for confirming some of my thoughts on this! I struggled to understand your final example for a little bit because I didn’t notice the function had been changed to bring in a property element as well. That seems like a really handy bit of code though!

I have a quick aside question if either of you could confirm my understanding with this… Within the function, in this instance, we can essentially use ‘obj’ & ‘users’ interchangeably seeing as we only have the one object to worry about. If we had multiple objects we’d be best off referring to ‘obj’ in our for/in statements to allow the function to work correctly across all objects correctly?

1 Like

I think in the sense of readability it’s more useful to use obj in the for loop
instead of users. I corrected it in my code,

users and obj are refering to the same object in memory (console.log(obj === users); // true).

The names inside the for…in loop are just like variables in a regular for loop. You can name the variables anything you want.

function countOnline (bunchOfClowns){
  for (let sillyUser in bunchOfClowns) {}
...
}

Although it’s best to name your variables according to usage to make reading your code easier.

function countOnline (mapOfUsers) {
 for (let user in mapOfUsers) {}
...
}

I’m not entirely sure if I answered your question, so let me know if I didn’t.