Meal Maker - another error in the addDishToCourse method

https://www.codecademy.com/paths/web-development/tracks/web-dev-js-arrays-loops-objects/modules/learn-javascript-objects/projects/meal-maker

My problem with this exercise seems very similar to others on this topic but I haven’t found an answer that works. Here’s my code:

const menu = {
  _courses: {
    _appetizers:[],
    _mains:[],
    _desserts:[]
  },
  get appetizers(){
    return this._courses._appetizers;
  },
  set appetizers(appetizers){
    this._courses._appetizers = appetizers;
  },
  get mains(){
    return this._courses._mains;
  },
  set mains(mains){
     this._courses._mains = mains;
  },
  get desserts(){
    return this._courses._desserts;
  },
  set desserts(desserts){
    this._courses._desserts = desserts;
  },

get courses() {
  return {
    appetizer: this._appetizers,
    main: this._mains,
    dessert: this._desserts
    }
  },
addDishToCourse(courseName,dishName,dishPrice){
  const dish = {
    name: dishName,
    price: dishPrice,
  };
 //logging courseName to try to debug the line below
 console.log(this._courses, courseName)
 
   this.courses[courseName].push(dish);
  },

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

generateRandomMeal() {
  const appetizer = this.getRandomDishFromCourse('appetizer');
  const main = this.getRandomDishFromCourse ('main');
  const dessert = this.getRandomDishFromCourse ('dessert');
  const totalPrice = appetizer.dishPrice + main.dishPrice + dessert.dishPrice;

  console.log(`Your appetizer will be ${appetizer.name}, your main will be ${main.name}, your dessert will be ${dessert.name} and the total price will be ${totalPrice}.`);
   
  }
}


menu.addDishToCourse('appetizer','mushroom pate on sourdough toast','£4.50');
menu.addDishToCourse('appetizer','beetroot and kale salad with pine nuts','£4.25');
menu.addDishToCourse('appetizer','cream of celery soup','£4.75');

menu.addDishToCourse('main','aubergine curry with roasted chickpeas','£9.50');
menu.addDishToCourse('main','Mrs Hadfield\'s risotto','£10.75');
menu.addDishToCourse('main','Veggie chilli bowl','£11.50');

menu.addDishToCourse('dessert','fresh strawberries with clotted cream and mint','£4.50');
menu.addDishToCourse('dessert','rhubarb crumble with custard','£4.75');
menu.addDishToCourse('dessert','chocolate mousse supreme','£5.50');

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

The error message I’m getting is:

this.courses[courseName].push(dish);
                           ^

TypeError: Cannot read property 'push' of undefined

and you can see in the code I’ve tried logging this._courses, courseName as recommended in another thread. The result is
{ _appetizers: [], _mains: [], _desserts: [] } 'appetizer'
which I think is how it should be??

Any ideas?

well, the key/property is appetizers while your courseName variable has a appetizer value, which doesn’t match the key/property.

So close to solve the problem :wink:

Thanks @stetim94. That did look close but I was a bit confused which ‘s’ to remove, so I tried removing all of them - unless it’s a string, all my appetizers, mains and desserts are now single. But I still get the same error! This time I have tried logging _appetizer which returns undefined. Is that the problem or would it be undefined anyway at this stage in the process?

const menu = {
  _courses: {
    _appetizer:[],
    _main:[],
    _dessert:[]
  },
  get appetizer(){
    return this._courses._appetizer;
  },
  set appetizer(appetizer){
    this._courses._appetizer = appetizer;
  },
  get main(){
    return this._courses._mains;
  },
  set main(main){
     this._courses._main = main;
  },
  get dessert(){
    return this._courses._dessert;
  },
  set dessert(dessert){
    this._courses._dessert = dessert;
  },

get courses() {
  return {
    appetizer: this._appetizer,
    main: this._main,
    dessert: this._dessert
    }
  },
addDishToCourse(courseName,dishName,dishPrice){
  const dish = {
    name: dishName,
    price: dishPrice,
  };
 //trying to debug by logging things here:
 console.log(this._appetizer);
   this.courses[courseName].push(dish);
  },

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

generateRandomMeal() {
  const appetizer = this.getRandomDishFromCourse('appetizer');
  const main = this.getRandomDishFromCourse ('main');
  const dessert = this.getRandomDishFromCourse ('dessert');
  const totalPrice = appetizer.dishPrice + main.dishPrice + dessert.dishPrice;

  console.log(`Your appetizer will be ${appetizer.name}, your main will be ${main.name}, your dessert will be ${dessert.name} and the total price will be ${totalPrice}.`);
   
  }
}


menu.addDishToCourse('appetizer','mushroom pate on sourdough toast','£4.50');
menu.addDishToCourse('appetizer','beetroot and kale salad with pine nuts','£4.25');
menu.addDishToCourse('appetizer','cream of celery soup','£4.75');

menu.addDishToCourse('main','aubergine curry with roasted chickpeas','£9.50');
menu.addDishToCourse('main','Mrs Hadfield\'s risotto','£10.75');
menu.addDishToCourse('main','Veggie chilli bowl','£11.50');

menu.addDishToCourse('dessert','fresh strawberries with clotted cream and mint','£4.50');
menu.addDishToCourse('dessert','rhubarb crumble with custard','£4.75');
menu.addDishToCourse('dessert','chocolate mousse supreme','£5.50');

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

Thanks for your help!

Then the first step be should be to figure out what to do. Don’t just randomly do stuff

given an array will very likely contain multiple values, you should go with plural name for the properties/keys of the courses objects.

menu doesn’t have a _appetizer property or getter. So that is why you get undefined

same for your courses getter:


get courses() {
  return {
    appetizer: this._appetizer,
    main: this._main,
    dessert: this._dessert
    }
  },

menu object doesn’t have _appetizer, _main and _dessert property or getter. So those will be undefined as well.

What is the plan? How are you going to use getter and setters?

Thanks - that is really helpful advice as trying random stuff is very tempting. But I’m afraid I’m still stuck.

Finding it difficult to think logically because I can’t see how courses, identified by courseName which is a string, get added to the right course.

I’ve looked at the other posts in this topic that had the same error message and can’t see that I’m making their errors. I must still be missing something.

const menu = {
  _courses: {
    _appetizers:[],
    _mains:[],
    _desserts:[]
  },
  //step 4 
  get appetizers(){
    return this._courses._appetizers;
  },
  set appetizers(appetizerIn){
    this._courses._appetizers = appetizerIn;
  },
  get mains(){
    return this._courses._mains;
  },
  set mains(mainIn){
     this._courses._mains = mainIn;
  },
  get desserts(){
    return this._courses._desserts;
  },
  set desserts(dessertIn){
    this._courses._desserts = dessertIn;
  },
//step 5 & 6
get courses() {
   return {
    appetizers: this._courses._appetizers,
    mains: this._courses_mains,
    desserts: this._courses._desserts
    }
  },
//step 7
addDishToCourse(courseName,dishName,dishPrice){
  const dish = {
    name: dishName,
    price: dishPrice,
  };
console.log(this.appetizers);
//returns empty array as push is not working
   this._courses[courseName].push(dish)
  },

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

generateRandomMeal() {
  const appetizer = this.getRandomDishFromCourse('appetizer');
  const main = this.getRandomDishFromCourse ('main');
  const dessert = this.getRandomDishFromCourse ('dessert');
  const totalPrice = appetizer.dishPrice + main.dishPrice + dessert.dishPrice;

  console.log(`Your appetizer will be ${appetizer.name}, your main will be ${main.name}, your dessert will be ${dessert.name} and the total price will be ${totalPrice}.`);
   
  }
}


menu.addDishToCourse('appetizer','mushroom pate on sourdough toast','£4.50');
menu.addDishToCourse('appetizer','beetroot and kale salad with pine nuts','£4.25');
menu.addDishToCourse('appetizer','cream of celery soup','£4.75');

menu.addDishToCourse('main','aubergine curry with roasted chickpeas','£9.50');
menu.addDishToCourse('main','Mrs Hadfield\'s risotto','£10.75');
menu.addDishToCourse('main','Veggie chilli bowl','£11.50');

menu.addDishToCourse('dessert','fresh strawberries with clotted cream and mint','£4.50');
menu.addDishToCourse('dessert','rhubarb crumble with custard','£4.75');
menu.addDishToCourse('dessert','chocolate mousse supreme','£5.50');

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

Then this should be the focus. Lets go back to basics, when we have an object (myObj) with a property (myProp):

const myObj = {
   myProp: 3,
};

there are two ways we can get to the value, using the property:

console.log(myObj.myProp);

or using the key/associative array notation, where the key is a string:

console.log(myObj['myProp'];

this second approach is also what we use in this meal maker project.

the property notation doesn’t work with string, so we can’t use the property notation when we want to use a parameter (of a function/method).

Thanks again for your patience and for the hints above, which I understand in theory but have failed to see how to apply. After a day off I have tackled this again.

I have tried using .push within my setter methods and then a conditional which calls each of them in addDishToCourse, simply because that makes more sense to me, but my arrays are still undefined. I’m going to need a little bit more information to get to the end of this. Glad of the opportunity to stretch and concentrate on understanding one thing properly but there must be a limit to how much time I can spend on one problem!

const menu = {
  _courses: {
    _appetizers:[],
    _mains:[],
    _desserts:[]
  },
  //step 4 
  get appetizers(){
    return this._courses._appetizers;
  },
  
  get mains(){
    return this._courses._mains;
    },

  get desserts(){
    return this._courses._desserts;
    },

  set appetizers(appetizer){
    this._courses._appetizers.push(appetizer);
  },
  set mains(main){
     this._courses._mains.push(main);
  },
  set desserts(dessert){
    this._courses._desserts.push(dessert);
  },
//step 5 & 6
get courses() {
   return {
    appetizers: this.appetizers,
    mains: this.mains,
    desserts: this.desserts
    };
  },
//step 7
addDishToCourse(courseName,dishName,dishPrice){
  const dish = {
    name: dishName,
    price: dishPrice,
  };
   //this.courses[courseName] = dish;

   //trying a conditional to definitely link courseName to the right string?
   if (courseName === 'appetizer') {
     this.appetizers = this.dish;
   }
   else if (courseName === 'main') {
     this.mains = this.dish;
   }
   else {this.desserts = this.dish;}
   //has anything been added to an array?
   console.log(this._appetizers);
  },


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

generateRandomMeal() {
  const appetizer = this.getRandomDishFromCourse('appetizer');
  const main = this.getRandomDishFromCourse ('main');
  const dessert = this.getRandomDishFromCourse ('dessert');
  const totalPrice = appetizer.dishPrice + main.dishPrice + dessert.dishPrice;

  console.log(`Your appetizer will be ${appetizer.name}, your main will be ${main.name}, your dessert will be ${dessert.name} and the total price will be ${totalPrice}.`);
   
  }
}


menu.addDishToCourse('appetizer','mushroom pate on sourdough toast','£4.50');
menu.addDishToCourse('appetizer','beetroot and kale salad with pine nuts','£4.25');
menu.addDishToCourse('appetizer','cream of celery soup','£4.75');

menu.addDishToCourse('main','aubergine curry with roasted chickpeas','£9.50');
menu.addDishToCourse('main','Mrs Hadfield\'s risotto','£10.75');
menu.addDishToCourse('main','Veggie chilli bowl','£11.50');

menu.addDishToCourse('dessert','fresh strawberries with clotted cream and mint','£4.50');
menu.addDishToCourse('dessert','rhubarb crumble with custard','£4.75');
menu.addDishToCourse('dessert','chocolate mousse supreme','£5.50');

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

And my current error message:

undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
undefined
/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:60
  const randomIndex = Math.floor(Math.random() * dishes.length);
                                                       ^

TypeError: Cannot read property 'length' of undefined
    at Object.getRandomDishFromCourse (/home/ccuser/workspace/learn-javascript-objects-meal-maker/app.js:60:56)

I like how you use the setters for .push()

However, here:

if (courseName === 'appetizer') {
     this.appetizers = this.dish;
   }
   else if (courseName === 'main') {
     this.mains = this.dish;
   }
   else {this.desserts = this.dish;}

this is the object you currently work on, which doesn’t have a dish property.

you declare dish within this method:

  const dish = {
    name: dishName,
    price: dishPrice,
  };

so you can just do:

   if (courseName === 'appetizer') {
     this.appetizers = dish;
   }

finally, here:

   //has anything been added to an array?
   console.log(this._appetizers);

this (menu object) doesn’t have an _appetizers property. (this property is nested in the _courses object. You could use this.appetizers, which will use the getter.

Do you know why we prefix properties that use a getter with an underscore?

lets say we don’t add an underscore, and attempt to implement a getter:

const myObj = {
   a: 3,
   get a()
   {
       return this.a;
   }
};

console.log(myObj.a);

now the getter will just keep calling itself. Any time we try to access the property in the getter, the property will use the getter.

which is why we prefix the property with an underscore. Its just a naming convention. We could also do:

const myObj = {
   a: 3,
   get b()
   {
       return this.a;
   }
};

console.log(myObj.b);

but that would just be very confusing.

So, recap:

When working inside an object or method, we can access properties and methods using this (which is the current object)

Sidenote: I think JS also makes life more difficult, when a property doesn’t exist, JS should just throw an error. Instead, JS gives undefined, making it really difficult to determine where the undefined comes from

Thank you! That does look much better because there are appetizers in my array - lots of them. But the error in getRandomDishFromCourse - seems it can’t identify dishes which it should pick up from the courses getter method - is this not working because I changed the way addDishToCourse works?

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

error message:

  const randomIndex = Math.floor(Math.random() * dishes.length);
                                                       ^

TypeError: Cannot read property 'length' of undefined
    at Object.getRandomDishFromCourse

seems we are getting somewhere :slight_smile:

well, in generateRandomMeal you do:

  const appetizer = this.getRandomDishFromCourse('appetizer');
  const main = this.getRandomDishFromCourse ('main');
  const dessert = this.getRandomDishFromCourse ('dessert');

which is a problem.

your courses getter (which you use in getRandomDishFromCourse), returns an object with plural names (so appetizers) and so forth

When having errors like this, you should log the object and the key/property you try to use:

console.log(this.courses, courseName);
const dishes = this.courses[courseName];

to help you identify the problem

Yay! I managed one final debug on my own, of the totalPrice method, and it’s working. Thank you @stetim94 I can now enjoy the rest of the day :smiley:

1 Like