Meal Maker undefined object lists, despite being intialized right there and exactly like the hint

Currently doing the Meal Maker project: https://www.codecademy.com/paths/create-video-games-with-phaser/tracks/game-dev-learn-javascript-objects/modules/game-dev-learn-javascript-objects/projects/meal-maker

Everything seemed to be going fine up until step 12 where you use a method to .push items into the following object arrays:

const menu = 
{
  _courses: 
  {
    appetizers: [],
    mains: [],
    desserts: []
  },
//(...)
}

At which point it gives an error because it can’t use .push on undefined, and when I console log menu._courses it indeed shows the value to each key is undefined instead of an empty list and I just can’t figure out what is wrong, especially since the hint in the exercise uses the exact same syntax.

console.log(menu._courses) is printing:

{ appetizers: undefined, mains: undefined, desserts: undefined }

I have tried assigning just integer values instead of empty arrays to the keys to console log and see if they get printed properly and it still just prints undefined, which is extremely confusing and goes against everything I thought I had learned in the course so far. Why on Earth do key value pairs usually print just fine but then for nested objects it all goes out the window like this?

If I place the exact same empty arrays as keys from menu itself they print out as empty arrays just fine when I console.log(menu). Would be nice to have been given a primer on this kind of shenanigans before being tossed into the project like this. I assume it must be something to do with this usage but the (very brief) lesson on it did absolutely not explain it well enough as it never said anything about nested objects and keys not being promptly initiated or accesible in such cases.

Maybe something else is setting those values to undefined? Can you provide the entire code and we’ll try to work this out!

Sure, here’s the code. But shouldn’t console.log(menu._courses) print out the empty arrays as they should, just given the above snippet?

Edit: erased the getters and setters bit cause they just bloat the code and don’t really do anything in this project. The error persists still the same.

const menu = {
  _courses: {
    appetizers: [],
    mains: [],
    desserts: []
  },

  get _courses(){
    return {
      appetizers: this.appetizers,
      mains: this.mains,
      desserts: this.desserts,
    }
  },

  addDishToCourse(courseName, dishName, dishPrice){
    const dish ={
      name: dishName,
      price: dishPrice
    };
    this._courses[courseName].push(dish);
  },

  getRandomDishFromCourse(courseName){
    const dishes = this._courses[courseName];
    const index = Math.floor(Math.random() * dishes.length);
    return dishes[index];
  },

  generateRandomMeal(){
    const appetizer = this.getRandomDishFromCourse("appetizers");
    const main = this.getRandomDishFromCourse("mains");
    const dessert = this.getRandomDishFromCourse("desserts");
    const totalPrice = appetizer.price + main.price + dessert.price;

    return `Your meal is ${appetizer.name}, ${main .name} and ${dessert .name}. The price is $${totalPrice}.`
  }
};

console.log(menu._courses);

Question: if you are using menu.addDishToCourse() to add dishes to _courses, but you console.log(menu._courses) before you have added anything (your console.log() is declared before the other lines), does it make sense that at that point in execution menu._courses is empty? :thinking:

I’ve learned to never assume when it comes to coding haha. There is nothing wrong with the snippet and, in fact, those values are empty arrays. The problem is you are trying to access something that does not exist - not what you think you’re accessing.

Menu doesn’t know what appetizers, mains, or desserts are - but _courses does. You’ll have to chain _courses in these statements to access the properties.

Being empty would be fine if it was showing as an empty array, but instead it is showing as undefined which is where the whole problem lies.

I tried that before I posted here but then it throws me a different error. I was assuming that as a getter, it didn’t require this to access _courses itself, but rather what is inside _courses. But of course with the arrays not even being initialized, verifying that becomes problematic. It is beside the point anyway, as that isn’t used anywhere in the project. The later steps of the project only really use .addDishToCourse, .getRandomDishFromCourse and .generateRandomMeal methods.

I tried using javascript Object.methods to try and print the menu._courses arrays, but no matter what they are always undefined. I am absolutely stumped on this and not even knowing where to look to find where the problem lies only makes it that much more frustrating.

Alright, I think I figured it out. You like putting the brackets in the next line after defining a method like addDishToCourse.

Move the { to the same line where you are declaring the methods.
Like this:

addDishToCourse(courseName, dishName, dishPrice) {
    const dish =
    {

For further reading, google “implicit semicolon insertion in javascript”

Yeah I find it helps readability for me. The only time it gave any trouble was for when the curly brackets came after a return (for which I now know to avoid doing this), otherwise it’s been working fine for everything I’ve done thus far. I tried putting all opening brackets in the same line as whatever required them but the problem persists, unfortunately.

Edited the previous post with the code accordingly.

oh I thought you said you deleted all your getters and setters. I see you still have your get _courses in there. That doesn’t work because you are adding _ before courses. We use _ to indicate that property shouldn’t be directly changed as a convention, but your internal methods don’t need it.

Delete your get _courses method, or simply erase that underscore prefix and leave it as get courses.

Also, don’t forget to use menu.addDishToCourse() before trying to console.log menu

1 Like

Ah yea I think that was the real problem - good catch! Didn’t realize when I was testing it on my end that I deleted the underscore! :laughing:

Let’s look at what is happening. When you console.log(menu._courses);, you are expecting:

But, that isn’t what you asked for. As pointed out by @irlfede and @ryansup, you have this getter:

When you reference menu._courses, the getter is invoked, so instead of the menu._courses object you were expecting, you are getting this:

Which prints to the console like this:

{ appetizers: undefined, mains: undefined, desserts: undefined }

Why are the values undefined? Well, this refers to menu. There is no menu.appetizers, etc. To return the object with the empty arrays that you had already defined, you would need to properly refer to them:

All of that said, you are actually lucky that you didn’t include this._courses.appetizers, etc. like this snippet above. Here we have a circular reference. When this._courses is executed, the getter, get _courses() is invoked which in turn executes this._courses again which then invokes the getter again, etc. forever. Dropping the _ from the getter (get courses())is the solution. Then you can use the getter like so:

console.log(menu.courses);
3 Likes

Ah, I see. I didn’t think that getter was doing anything, so clearly I misunderstood how getters work. That was indeed the culprit, thank you.

I wasn’t aware of that, was expecting it to just print the object directly without going through the getter. So that’s what was confusing me so much. Thank you, as well, would definitely not have figured that out.

This part also confuses me. As I said I had tried that before and gotten the RangeError: Maximum call stack size exceeded. It was my understanding from the lessons that the underscore is merely a convention to denote a value which shouldn’t be changed. So how come the underscore has such an effect in the code itself? Why does this infinite loop not happen on other getters without the underscore? Or is it just because they aren’t getting nested objects?

And don’t getters have to match the key string of an object to work?

Getters and setters can be named whatever you want but what matters is the code within them:

const obj = {
	_name: 'Ryan',
	get booty() {
		return this._name;
	}
}
console.log(obj.booty); // Prints Ryan

I’m returning my _name even though the getter is called booty. It just makes sense to name them what you want to retrieve.

Getter methods can be called without having () at the end of them. If the getter is named the same as the property, the getter thinks it is calling itself when you say return this._courses.appetizers, so it calls the get _courses method again, and it keeps repeating that cycle.

The infinite loop doesn’t happen if the property and getter do not share a name.

2 Likes

Ooooh, that clarifies a lot! I completely misunderstood how getters work. Thank you very much, it all makes sense now!

1 Like

In addition to @ryansup’s very good explanation, I’d like to point out that there is nothing special about the _ itself. Placing the _ at the beginning of a property name is merely a naming convention that signals to other humans that the property should be accessed using the getter and setter methods. The following code would work, but since it doesn’t follow commonly accepted practices, co-workers, colleagues, etc. would find it odd or confusing.

const someObject = {
  name: "Tod",
  age: 100, 
  get spaghetti() {
    return this.name;
  },
  set tacos(bologna) {
    this.name = bologna;
  },
  get lasagna() {
    return this.age;
  },
  set burritos(chili) {
    this.age = chili;
  }
}
//view the properties using the getters
console.log(someObject.spaghetti); // prints "Tod"
console.log(someObject.lasagna); // prints 100
//use the setters:
someObject.tacos = "Homer";
someObject.burritos = 50;
//view changes using the getters again:
console.log(someObject.spaghetti); // prints "Homer"
console.log(someObject.lasagna); // prints 50

It’s so confusing, I had to keep scrolling up to remember what was what, but it works. :slightly_smiling_face:

Just to show there is no difference between _ and any other character, the following works too:

const _ = {
  __: "Tod",
  ___: 100, 
  get ______() {
    return this.__;
  },
  set ________(____) {
    this.__ = ____;
  },
  get _______() {
    return this.___;
  },
  set _________(_____) {
    this.___ = _____;
  }
}
//view the properties using the getters
console.log(_.______); // prints "Tod"
console.log(_._______); // prints 100
//use the setters:
_.________ = "Homer";
_._________ = 50;
//view changes using the getters again:
console.log(_.______); // prints "Homer"
console.log(_._______); // prints 50
2 Likes