Meal Maker: cannot read property 'price' of undefined

Project can be found here.

I’ve been trying this for an hour or two now. I tried looking at hints, other threads, and watching the provided video but no solution (even doing it along with the video!) has worked – sometimes I’ll fix one thing but I haven’t been able to actually run the code.

Here’s what I have right now:

const menu = {
  _courses: {
    appetizers: [],
    mains: [],
    desserts: [],
  },
  set appetizers(appetizer) {
    this.appetizers.push(appetizer);
  },
  get appetizers() {
    return this.appetizers;
  },
  set mains(main) {
    this.mains.push(main);
  },
  get mains() {
    return this.mains;
  },
  set desserts(dessert) {
    this.desserts.push(dessert);
  },
  get desserts() {
    return this.desserts;
  },
  get courses() {
    return {
      appetizers: this._courses.appetizers,
      mains: this._courses.mains,
      desserts: this._courses.desserts,
    };
  },
  addDishToCourse(courseName, dishName, dishPrice) {
    const dish = {
      name: dishName,
      price: dishPrice,
    };
    return this._courses[courseName] = dish;
  },
  getRandomDishFromCourse(courseName) {
    const dishes = this._courses[courseName];
    const randomIndex = Math.floor(Math.random() * dishes.length);
    return dishes[randomIndex];
  },
  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 consists of: ${appetizer.name}, ${main.name}, and for dessert, ${dessert.name}. The total price is ${totalPrice}.`
  },
};
menu.addDishToCourse('appetizers', 'Fried Mozzarella', 6.00);
menu.addDishToCourse('appetizers', 'Fried Pickles', 5.50);
menu.addDishToCourse('appetizers', 'Barbecue Chicken Flatbread', 8.25);

menu.addDishToCourse('mains', 'Chicken Piccata', 15.00);
menu.addDishToCourse('mains', 'Shrimp Scampi', 18.50);
menu.addDishToCourse('mains', 'Penne alla Vodka', 14.50);

menu.addDishToCourse('desserts', 'Brownie Sundae', 6.50);
menu.addDishToCourse('desserts', 'Tiramisu', 5.00);
menu.addDishToCourse('desserts', 'Pecan Pie', 5.25);

const meal = menu.generateRandomMeal();
console.log(meal);

And my current error is:

/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:48
    const totalPrice = appetizer.price + main.price + dessert.price;
                                ^

TypeError: Cannot read property 'price' of undefined
    at Object.generateRandomMeal (/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:48:33)
    at Object.<anonymous> (/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:64:19)
    at Module._compile (module.js:571:32)
    at Object.Module._extensions..js (module.js:580:10)
    at Module.load (module.js:488:32)
    at tryModuleLoad (module.js:447:12)
    at Function.Module._load (module.js:439:3)
    at Module.runMain (module.js:605:10)
    at run (bootstrap_node.js:427:7)
    at startup (bootstrap_node.js:151:9)

I’d be so grateful if anyone can tell what I’ve done wrong!

1 Like

a debug tool would be better, but lets see if we can find where undefined is coming from:

const appetizer = this.getRandomDishFromCourse('appetizers');
console.log(appetizer);

okay, so getRandomDishFromCourse returns undefined, lets go inspect this function:

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

problem: randomIndex is NaN, how could that be? And dishes variable is an object, shouldn’t it be an array?

So something also goes wrong when trying to add the dishes. It seems you don’t push to array properly

2 Likes

Thank you for responding so quickly! All I did was change

return this._courses[courseName] = dish;

to

this._courses[courseName].push(dish);

and now it works perfectly. :grinning:

1 Like

good :slight_smile: But i also hoped you learned something about bug fixing and tracking a bug, the thinking steps i displayed

2 Likes

I am getting the same problem, can someone help?
Here is my code:

const menu = {
  _courses: {
    appetizers: [],
    mains: [],
    desserts: [],
  },
  set appetizers(appetizer) {
    this.appetizers.push(appetizer);
  },
  get appetizers() {
    return this.appetizers;
  },
  set mains(main) {
    this.mains.push(main);
  },
  get mains() {
    return this.mains;
  },
  set desserts(dessert) {
    this.desserts.push(dessert);
  },
  get desserts() {
    return this.desserts;
  },
  get courses() {
    return {
      appetizers: this._courses.appetizers,
      mains: this._courses.mains,
      desserts: this._courses.desserts,
    };
  },
  addDishToCourse(courseName, dishName, dishPrice) {
    const dishes = {
      name: dishName,
      price: dishPrice,
    };
    this._courses[courseName] = dishes;
  },
  getRandomDishFromCourse(courseName) {
    const dishes = this._courses[courseName];
    const randomIndex = Math.floor(Math.random() * dishes.length);
    return dishes[randomIndex];
  },
  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 consists of: ${appetizer.name}, ${main.name}, and for dessert, ${dessert.name}. The total price is ${totalPrice}.`
  },
};
menu.addDishToCourse('appetizers', 'Mandazi', 4.00);
menu.addDishToCourse('appetizers', 'Pita bread', 3.50);
menu.addDishToCourse('appetizers', 'Salad', 4.25);

menu.addDishToCourse('mains', 'Soup', 5.00);
menu.addDishToCourse('mains', 'Boiled Chicken', 12.50);
menu.addDishToCourse('mains', 'Githeri', 9.00)

menu.addDishToCourse('desserts', 'Ice Cream', 3.50);
menu.addDishToCourse('desserts', 'Pop', 3.00);
menu.addDishToCourse('desserts', 'Choc', 4.50);

const meal = menu.generateRandomMeal();
console.log(meal);

And this is the error:

/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:48
const totalPrice = appetizer.price + main.price + dessert.price;
^

TypeError: Cannot read property ‘price’ of undefined
at Object.generateRandomMeal (/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:48:33)
at Object. (/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:64:19)
at Module._compile (module.js:571:32)
at Object.Module._extensions…js (module.js:580:10)
at Module.load (module.js:488:32)
at tryModuleLoad (module.js:447:12)
at Function.Module._load (module.js:439:3)
at Module.runMain (module.js:605:10)
at run (bootstrap_node.js:427:7)
at startup (bootstrap_node.js:151:9)

okay, so appetizer variable in generateRandomMeal method is undefined, which comes from getRandomDishFromCourse method:

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

oops? dishes is an object, accessing objects by index doesn’t go well:

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

which gives undefined. appetizers (mains and desserts) should be an array containing objects.

even though you add 3 dishes per course, only one shows up, so something doesn’t go right when attempting to push into the array

1 Like
  addDishToCourse(courseName, dishName, dishPrice) {
    const dishes = {
      name: dishName,
      price: dishPrice,
    };
    this._courses[courseName] = dishes; // This should be this._courses[courseName].push(dishes);

The way you have it you replace the existing array with your new dishes object instead of adding it to the array.

2 Likes

That line will work when it actually uses the setter, which in this case it clearly does not. That is why the array is overwritten.

There is a problem that the video does not address which will need some careful investigation and tweaking of the code.

Some things that come to mind are,

  • the courses properties supposedly have setters and getters but we know they are not working.
  • the setters and getters are written in menu context, not _courses context so they do not see the properties they are supposed to reference.
  • the properties should be written as backing variables to prevent circular references.
  • the setters and getters need to be in the same context as the properties.

Consider,

  _courses: {
    _appetizers: [],
    _mains: [],
    _desserts: [],
    set appetizers(appetizer) {
      this._appetizers.push(appetizer);
    },
    get appetizers() {
      return this._appetizers;
    },
    set mains(main) {
      this._mains.push(main);
    },
    get mains() {
      return this._mains;
    },
    set desserts(dessert) {
      this._desserts.push(dessert);
    },
    get desserts() {
      return this._desserts;
    }
  },
  get courses() {
    return {
      appetizers: this._courses.appetizers,
      mains: this._courses.mains,
      desserts: this._courses.desserts,
    };
  },
  ...

Before running any other lines of code, (comment them all out), run this one line…

console.log(menu.courses)

We should see an object consisting of three arrays.

{appetizers: Array(0), mains: Array(0), desserts: Array(0)}

Now run the three lines that load up the appetizers, and repeat the above command.

We should see,

  1. {appetizers: Array(3), mains: Array(0), desserts: Array(0)}

  2. appetizers: Array(3)

  3. 0: {name: “Mandazi”, price: 4}

  4. 1: {name: “Pita bread”, price: 3.5}

  5. 2: {name: “Salad”, price: 4.25}

or something similar that confirms our items have been pushed to the array.

Note that this is the line we are using in the addDishToCourse method…

this._courses[courseName] = dishes;

Thank you all. It works courtesy of Midlindner. You’re all awesome geeks! :kissing_heart:

2 Likes

Did you make it work with push in the addDishToCourse? If so, humor us and completely remove all the setters and getters (leave get courses) and see if the code still works (it should). What was the point of having them if they are not verified as working?

@alyssavigil, it would be so nice to have the course author weigh in here.

1 Like

Hey everyone, I’m Kenny, a Curriculum Developer here at Codecademy.

While I didn’t write this project, I did want to speak to a few things in this project and this thread overall.

First off, thanks everyone who contributed and made this a discussion about debugging, working together to find out ways to improve the content, and vocalizing the pain points. It’s great to see such collaboration and care and helps us all learn :slight_smile:

I want to address the code here that I saw for both code snippets provided by separate learners:

const menu = {
  _courses: {
    appetizers: [],
    mains: [],
    desserts: [],
  },
  set appetizers(appetizer) {
    this.appetizers.push(appetizer);
  },
  // ...
}

When access this.appetizers, in this case, menu.appetizers, we don’t actually have a appetizers property, it’d automatically have a value of undefined. So whenever the setter is being used, it’s going to throw an error since we’re calling .push() on undefined. This pattern also exists for the other setters in the menu object. The code should read: this._courses.appetizers.push(appetizer)

As far as this project is concerned, I do agree on points where we need to do a better job of motivating the usage of getters and setters. Currently, there is no error checking or real incentive for us to abide by getters or setters and access menu._courses directly.

I would also like to have an earlier step in which we console.log(menu) earlier to check what’s in our object as we call our setter, getters, and other methods (just to check that it’s working).

There is a good amount of work and love that needs to go in this existing project and I’d be happy to tackle this once some time frees up. In the meanwhile, we’ll be considering what additional changes have to be made and keep track of these before we can implement these changes.

Thanks again to everyone who chimed in here, it’s always great to see such productive conversations!

2 Likes

menu_object

That would go to support the presented. But it also points to a way to insulate against circular references. _courses is still a backing variable for its getter, but everything within the reference is plain object. Here we have a setter given in one context that behaves in another; and, it does not need a getter.

We’ll need to chase down ALL the rabbit holes on this concept now the cat is out of the bag, so to speak…

3 Likes