Meal Maker - Setters and Getters

In the meal maker project, I have no idea what the point of the setters and getters were. I removed them and the code worked perfectly fine without them, so what was the point of the project asking for them in the first place? Please help me understand! :smile:

Meal maker project

This was my code without the setters and getters:

const menu = {
_courses: {
appetizers: ,
mains: ,
desserts:
},
addDishToCourse(courseName, name, price) {
const dish = {
name,
price
};
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(‘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} for appetizers, ${main.name} as your main course, and ${dessert.name} for desert, and the total price is $${totalPrice}.;
}
};
menu.addDishToCourse(‘appetizers’,‘salad’, 4.00);
menu.addDishToCourse(‘appetizers’,‘sambosa’, 5.00);
menu.addDishToCourse(‘appetizers’,‘hummus’, 3.00);
menu.addDishToCourse(‘mains’,‘fish’, 10.00);
menu.addDishToCourse(‘mains’,‘steak’, 9.00);
menu.addDishToCourse(‘mains’,‘veggie noodles’, 8.00);
menu.addDishToCourse(‘desserts’,‘ice cream’, 5.50);
menu.addDishToCourse(‘desserts’,‘cake’, 7.00);
menu.addDishToCourse(‘desserts’,‘popcorn’, 2.00);
let meal = menu.generateRandomMeal();
console.log(meal);

The point of getters and setters are to give validation and to ensure that data you want added is being added. In this specific project no validation was entered, however you generally don’t want the properties to be directly edited as this could result in the code being broken with bad input, and you tend to mark these properties with a _ in front.
By adding getters and setters, you can run validation before adding data to your object properties, whereas without this, someone could just write a single number to the appetizers course and you couldn’t do anything about it. It’s just good practise to get into the habit of using them and marking your properties with an underscore, as there will be plenty of occasions where you need to validate the data before entering it.

2 Likes

Thank you! I think it’s starting to make more sense… can you please give me an example where validation would be entered?

An example is actually in the setter lesson just before that project. In this lesson you ensure that the value you are getting from _numOfSensors is a number, else an error message is printed, and then the same kind of validation for the setters. This isn’t validation you could have if you just entered straight up using the property assignment.

The benefit is you can use getters and setters exactly the same as if you were assigning to the property, so the user experience is identical, whilst the coder can cover for any issues that might arise from bad inputs.

1 Like

Ahhh yes, now I understand fully. Thank you for your help!

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

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

  get appetizers() {
    return this._courses.appetizers;
  },
  get mains() {
    return this._courses.mains;
  },
  get desserts() {
    return this._courses.desserts
  },
  set appetizers(appetizerIn) {
    this._courses.appetizers = appetizerIn;
  },
  set mains(mainIn) {
    this._courses.mains = mainIn;
  },
  set desserts(dessertIn) {
    this._courses.dessertIn = dessertIn;
  },

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

  getRandomDishFromCourse(courseName) {
    const dishes = this._courses[courseName];
    const randomIndex = Math.round(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 is ${appetizer.name}, ${main.name}, ${dessert.name}
    .The price is ${totalPrice}.`;
  }
}

menu.addDishToCourse('appetizers', 'App Caesar Salad', 4.25);
menu.addDishToCourse('appetizers', 'App Chicken Salad', 4.25);
menu.addDishToCourse('appetizers', 'App Vege Salad', 4.25);

menu.addDishToCourse('mains', 'Main Caesar Salad', 8.25);
menu.addDishToCourse('mains', 'Main Chicken Salad', 8.25);
menu.addDishToCourse('mains', 'Main Vege Salad', 8.25);

menu.addDishToCourse('desserts', 'Desserts Caesar Salad', 7.25);
menu.addDishToCourse('desserts', 'Desserts Chicken Salad', 7.25);
menu.addDishToCourse('desserts', 'Desserts Vege Salad', 7.25);

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

I’m getting an error :

TypeError: Cannot read property 'price' of undefined
    at Object.generateRandomMeal (/home/runner/Meal-Maker-getter-and-setter/index.
js:53:34)
    at /home/runner/Meal-Maker-getter-and-setter/index.js:71:19
    at Script.runInContext (vm.js:131:20)
    at Object.<anonymous> (/run_dir/interp.js:156:20)
    at Module._compile (internal/modules/cjs/loader.js:1133:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1153:10)
    at Module.load (internal/modules/cjs/loader.js:977:32)
    at Function.Module._load (internal/modules/cjs/loader.js:877:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)

Trying to understand why is it throwing Typeerror

Your problem is arising in the generation of the random dish from a course. If you look at your index being generated here

Then we can follow through and see the issue. First off, Math.random() should have the brackets, hence it is not calling correctly and you’re returning undefined from the getRandomDishFromCourse() function calls. This fixes your initial issue, however now you’ll find that you still get the error occasionally. This comes down to the other part of this line Math.round().

Math.random() will return a random number from 0 up to but not including 1. Hence when we multiply by dishes.length, which in this case is always 3, you are getting a number from 0 up to but not including 3. However if the number is above 2.5, then Math.round() will round this up to 3, and since array indexes begin at 0, an array of length 3 has indexes 0, 1 and 2. Therefore randomIndex = 3 will return the same undefined error, as there is no object in dishes[3]. As such we need to instead use Math.floor(), as this will always round down, which gives you a random integer that is either 0, 1 or 2, the 3 valid indexes. You should then find that your code works perfectly.

1 Like

Thanks a lot!
I’m still having a doubt as to when is getter function for courses (appetizers, mains, desserts) and getter function for _courses is being called. I seem to have understood the lesson momentarily but failed to correctly implement it in the porject. Had to get some help

It happens, a lot of people do have a tough time with the getters and setters, especially since at the smaller projects they can be functionally identical to just assigning properties directly. The main things to remember are

  1. If you are defining getters and setters, then remember the _ in front of the initial property declaration, and then using that inside the getter and setter definition only.
  2. As soon as your getters and setters have been defined they can be used anywhere after the definition, including inside the same object.

For an example on this one, you can change

to

get appetizers() {
   return this.courses._appetizers
}

and it should work exactly the same (assuming you change your property definition to _appetizers like recommended in my first point.

1 Like

Okay thanks, I looked in the youtube video attached for help with this project and didn’t find it, thanks for the suggestions. Is it always necessary for the method to return something?
Like over here,

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

Can’t we have this?

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

For methods no, there’s no need to return anything you can just carry out the function the way you see there. Returning can be useful as it can make debugging slightly easier, as if you wanted to check addDishToCourse() was adding the correct thing, then you can just do console.log(addDishToCourse(arg1, arg2, arg3) and see if it returns what you expect. In this case doing that should return the length of whatever course you have just pushed the dish to, so if you console.log() your first call, then it will return 1 and print 1 to the console. If you don’t use return, the function will still work correctly, however printing to the console will show undefined. It entirely depends on the situation and use case for the function if you should use return, but in the case of a function or method modifying something inside of it, you do not need a return.