Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit c7ae0cd

Browse files
165 Forkify: Recipe Model (Part 2)
- Added 7.7.10 named as "Building the Recipe (Data) Model [Part 2] Using the eval() and map() methods & Map object"
1 parent 6e59a56 commit c7ae0cd

File tree

2 files changed

+112
-11
lines changed

2 files changed

+112
-11
lines changed

‎Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/index.js‎

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
/********************************************************************************************************************
22
* What we'll learn:
33
* ----------------
4-
* 1. How to read data from the page URL.
5-
* 2. How to respond to the 'hashchange' event.
6-
* 3. How to add the same event listener to multiple events.
4+
* 1. Use array methods like map(), slice(), findIndex() and includes().
5+
* 2. How and why to use the eval() method.
76
*
8-
* Whenever we click on one of the recipes that is shown in .results_list class' element, we get the ID of the recipe
9-
* in the URL as: 'localhost:8080/?#46956'. Now the URL has a hash-link (after the '?') which has the ID
10-
* of the respective recipe we clicked. Therefore, we can take advantage of that using the 'hashchange' event
11-
* in the DOM, using an event listener for that event. The code is implemented below.
7+
* This time, we want to read through the list of ingredients and at the same time, separate the quantity
8+
* (count), unit and the description of each ingredient such that we can increase/decrease the quantities
9+
* of the ingredients depending on the number of servings the user wants.
10+
*
11+
* This is done by implementing the parseIngredients() method at ./src/js/models/Recipe.js module.
12+
* Look into the Recipe model module to know more about the parseIngredients() method.
1213
*
1314
*/
1415

@@ -109,11 +110,14 @@ const controlRecipe = async () => {
109110

110111
// Create new recipe object
111112
state.recipe = new Recipe(id);
113+
// window.r = state.recipe; // TEST CODE
114+
112115

113116
try {
114-
// Get the recipe data
117+
// Get the recipe data and parse ingredients
115118
await state.recipe.getRecipe();
116-
119+
state.recipe.parseIngredients();
120+
117121
// Calculate servings of the recipe and time required to make the recipe.
118122
state.recipe.calcServings();
119123
state.recipe.calcTime();
@@ -137,5 +141,3 @@ const controlRecipe = async () => {
137141
// Instead of adding the same event handler - controlRecipe() for two events on window, we can simply
138142
// do it in a single line using the forEach() method as shown below.
139143
['hashchange', 'load'].forEach(event => window.addEventListener(event, controlRecipe));
140-
141-
// Next, we will process the ingredients that we receive we have in the Recipe Model, at ./src/js/models/Recipe.js

‎Modern-JS-ES6-NPM-Babel-Webpack/forkify_project/src/js/models/Recipe.js‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,103 @@ export default class Recipe {
5959
// by just saying that every dish will serve 4 people no matter what.
6060
this.servings = 4;
6161
}
62+
63+
parseIngredients() {
64+
// We create 2 Maps where one map stores the units that can occur in plural form,
65+
// for eg: teaspoons, tablespoons, etc and then store their values into short-form
66+
// units, which are tsp, tbsp, etc, respectively. And another map for units that
67+
// can occur in singular form i.e., teaspoon, tablespoon, etc. We will convert all of
68+
// the singular units also into an their respective short-forms as show n above.
69+
const abbrUnits = ['tbsp', 'tsp', 'cup', 'oz', 'pound'];
70+
71+
const pluralUnits = new Map();
72+
pluralUnits.set('tablespoons', abbrUnits[0]);
73+
pluralUnits.set('teaspoons', abbrUnits[1]);
74+
pluralUnits.set('cups', 'cup');
75+
pluralUnits.set('ounces', abbrUnits[3]);
76+
pluralUnits.set('pounds', abbrUnits[4]);
77+
78+
const singularUnits = new Map();
79+
singularUnits.set('tablespoon', abbrUnits[0]);
80+
singularUnits.set('teaspoon', abbrUnits[1]);
81+
singularUnits.set('ounce', abbrUnits[3]);
82+
83+
const newIngredients = this.ingredients.map(el => {
84+
// 1. Uniform units
85+
let ingredient = el.toLowerCase();
86+
for (let [key, value] of pluralUnits.entries()) {
87+
if (ingredient.includes(key))
88+
ingredient = ingredient.replace(key, value);
89+
}
90+
91+
for (let [key, value] of singularUnits.entries()) {
92+
if (ingredient.includes(key))
93+
ingredient = ingredient.replace(key, value);
94+
}
95+
96+
// 2. Remove Parentheses --- Regex taken from:
97+
// https://stackoverflow.com/questions/4292468/javascript-regex-remove-text-between-parentheses
98+
ingredient = ingredient.replace(/\([^)]*\)/g, '');
99+
100+
// 3. Parse ingredients into count, unit and ingredient
101+
// The hardest part of parsing the ingredients is to separate out the count and units.
102+
// That's because in the list of ingredients, some have the count in-front
103+
// and some don't. Therefore, we have to be clever about the way we decouple the quantity
104+
// and the unit of the ingredient. A good way is to use the index of the unit
105+
// and then decouple the quantity from the unit.
106+
const arrIng = ingredient.split(' ');
107+
const unitIndex = arrIng.findIndex(el => abbrUnits.includes(el));
108+
109+
let objIng;
110+
if (unitIndex > -1) {
111+
// There is a unit -- the difficult one to implement.
112+
113+
// first we pick the quantity(s) before the units
114+
// Ex. if arrIng is ['4, '1/2', 'cup', ...] => arrCount is ['4', 1/2']
115+
// Ex. if arrIng is ['4', 'cup', ...] => arrCount is ['4']
116+
const arrCount = arrIng.slice(0, unitIndex);
117+
118+
let count;
119+
if (arrCount.length === 1)
120+
count = eval(arrIng[0].replace('-', '+')); // For the strings which are like: '1-1/2 cup'
121+
else {
122+
// Now, we have the elements of arrCount as strings, but we are also sure that they are
123+
// strings that are numeric in nature. Therefore, whenever we have such a situation, we can
124+
// simply call the eval() method on the string (which is an arithmetic expression), so that it
125+
// can evaluate the arithmetic expression passed to it.
126+
count = eval(arrCount.slice().join('+'));
127+
128+
// if arrCount is ['4', '1/2'], then the line above will convert into '4+1/2' and then that's
129+
// passed onto the eval() method which will then evaluate the expression as JS Code, and actually
130+
// the string as an arithmetic expression and return 4.5
131+
}
132+
133+
objIng = {
134+
count,
135+
unit: arrIng[unitIndex],
136+
ingredient: arrIng.slice(unitIndex + 1).join(' ')
137+
};
138+
139+
} else if (parseInt(arrIng[0], 10)) {
140+
// There is NO unit, but 1st element is a number
141+
objIng = {
142+
count: parseInt(arrIng[0], 10),
143+
unit: '',
144+
ingredient: arrIng.slice(1).join(' ')
145+
};
146+
} else if (unitIndex === -1) {
147+
// There is NO unit and NO number in 1st position
148+
objIng = {
149+
count: 1,
150+
unit: '',
151+
ingredient // In ES6, to mention 'ingredient: ingredient', we need not assign it, we simply
152+
// mention it. Therefore, 'ingredient: ingredient' can be written as 'ingredient'
153+
};
154+
}
155+
156+
return objIng;
157+
});
158+
159+
this.ingredients = newIngredients;
160+
}
62161
};

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /