An (Im)mutable Shopping List for a Delicious Pesto Pasta
Jonathan Kulak
Posted on August 21, 2024
Pesto pasta is proof that G-d exists
There are few things in life that give me more pleasure than a heaping helping of fresh pesto over homemade Capellini (Angel Hair). I'm a bonafide foodie - especially when it comes to Italian cuisine - and am always trying more complex recipes, but the simplicity and enjoyment of such a minimalist dish never ceases to satisfy. If I had the (mis)fortune of choosing a last meal it would be a tough decision between sushi and pesto over pasta, but I still think pesto pasta wins out in the end.
All this talk of pesto is making me hungry
What am I to do? Well, make pesto pasta of course. Sometimes you just gotta say, "Quando a Roma!"
Lets start by making a list of ingredients to buy from our friendly Italian Market, "Il Mercato di Giovanni." We will create our shopping list from two recipes using both immutable and mutable object arrays. While it would be more efficient to simply write out what we need, you know that this is just way more fun. I can tell you're starving for more on how we can program our way to pesto pasta, so let's dig right in. "Mangia Mangia!"
Creating separate pasta and pesto recipe arrays
We'll start by declaring variables for pastaRecipeArray
and pestoRecipeArray
, with each variable assigned to an array of objects, where each object represents an individual ingredient.
When we assign array values to each variable we use the Object.freeze() method to ensure that they are immutable. (more on this later)
Each recipe object has three properties, with key-value pairs as follows:
- 'name' = The name of the ingredient in the form of a 'string'
- 'recipe' = A value or values, in the form of an 'array', indicating which recipe(s) the ingredient is needed for (pasta, pesto, or both)
- 'price' = The price of the ingredient in USD, in the form of a 'number', using fairly unrealistic dummy content
(note: I've ommitted quantities and other details to keep things brief and relatively simple in this post. We could also implement these objects using JSON, but we are keeping things easy to digest here.)
The code to establish these arrays will look something like this:
const pastaRecipeArray = Object.freeze([
{ "name": "Eggs", "recipe": ["pasta"], "price": 6.99 },
{ "name": "Extra Virgin Olive Oil", "recipe": ["pasta", "pesto"], "price": 12.59 },
{ "name": "Kosher Salt", "recipe": ["pasta", "pesto"], "price": 7.89 },
{ "name": "Semolina Flour", "recipe": ["pasta"], "price": 12.95 }
])
const pestoRecipeArray = Object.freeze([
{ "name": "Basil", "recipe": ["pesto"], "price": 6.99 },
{ "name": "Black Pepper", "recipe": ["pesto"], "price": 9.99 },
{ "name": "Extra Virgin Olive Oil", "recipe": ["pasta", "pesto"], "price": 12.59 },
{ "name": "Kosher Salt", "recipe": ["pasta", "pesto"], "price": 7.89 },
{ "name": "Parmesan", "recipe": ["pesto"], "price": 15.99 },
{ "name": "Pine Nuts", "recipe": ["pesto"], "price": 13.98 }
])
You'll notice again that the recipe
key points to a value that is in the form of an array. We set it up this way because some ingredients are used in both recipes.
To test that pastaRecipeArray
is set up properly we can utilize the .forEach() method, a callback function used to iterate over each object in the array. Using ingredient
as the parameter, we can log it into the console as follows:
pastaRecipeArray.forEach((ingredient) => {
console.log(ingredient)
})
When you inspect the console you should see something like the following output:
Object {name: "Eggs", recipe: Array(1), price: 6.99}
Object {name: "Extra Virgin Olive Oil", recipe: Array(2), price: 12.59}
Object {name: "Kosher Salt", recipe: Array(2), price: 7.89}
Object {name: "Semolina Flour", recipe: Array(1), price: 12.95}
Similarly, we can log our pestoRecipeArray
like this:
pestoRecipeArray.forEach((ingredient) => {
console.log(ingredient)
})
Which results in the following:
Object {name: "Basil", recipe: Array(1), price: 6.99}
Object {name: "Black Pepper", recipe: Array(1), price: 9.99}
Object {name: "Extra Virgin Olive Oil", recipe: Array(2), price: 12.59}
Object {name: "Kosher Salt", recipe: Array(2), price: 7.89}
Object {name: "Parmesan", recipe: Array(1), price: 15.99}
Object {name: "Pine Nuts", recipe: Array(1), price: 13.98}
(note: When you see output such as Array(1)
and Array(2)
you would either want to rewrite the function to select those keys or simply click on the array in the console to see the details of what it contains.)
Creating a Shopping List Array
Now that we've established our recipe arrays we want to move on to the next step, creating a shopping list array. To do this we will want to combine our object arrays pastaRecipeArray
and pestoRecipeArray
into a new mutable variable named shoppingListArray
. We will do this using the spread operator ...
like so:
const shoppingListArray = [...pastaRecipeArray, ...pestoRecipeArray]
Now let's use the following console.log()
to see what our new list looks like. Moving forward we will log property values within the object rather than the full object, to remove some of the clutter. You will want to use this code to see how our list changes after each step of the process.
shoppingListArray.forEach((ingredient) => {
console.log(ingredient.name)
})
We can see that our lists have been joined together into one in the console, this time only logging each ingredient name
.
Eggs
Extra Virgin Olive Oil
Kosher Salt
Semolina Flour
Basil
Black Pepper
Extra Virgin Olive Oil
Kosher Salt
Parmesan
Pine Nuts
Immutable vs. mutable arrays
Why should we make pastaRecipeArray
and pestoRecipeArray
immutable? Making them immutable makes it so that we can't change their values after they are assigned. We don't want to just tear these recipes up. We want to save them for another glorious day. Those immutable family recipes need to live on, regardless of what we are about to write on our temporary, mutable shopping list.
We also want to be able to add or substract ingredients from our newly created shoppingListArray
to make this dish to our specific tastes, without affecting our original recipes of course.
Adding, replacing, and deleting ingredients
As you may have noticed, when we combined our pasta and pesto recipes into our shopping list we ended up with duplicates for "Extra Virgin Olive Oil" and "Kosher Salt". We don't need to buy these twice so let's get rid of them. There are fancier ways to eliminate duplicates, but for now we will use .splice() to remove the first Extra Virgin Olive Oil
object.
The .splice()
method destructively deletes or replaces elements in an array. The first parameter represents the first element we are deleting and the second parameter represents how many elements we want to delete from that start point. While "Extra Virgin Olive Oil" is the second object in the array, arrays start at '0', so technically the second object is represented by a '1'. Let's execute the following:
shoppingListArray.splice(1, 1)
In the console you will see that there is now only one "Extra Virgin Olive Oil" object. (note: If you try to use .splice()
or similar methods on one of our original recipe arrays you will get a TypeError because we used Object.freeze()
, making them immutable.)
We still have an extra "Kosher Salt", and now we are going to use .splice()
to it's full power. In addition to our first two parameters we have a third parameter that can replace elements in an array with new elements. I love to add a bit of lemon to my pesto, and I don't like food that is too salty, so let's go ahead and replace our extra "Kosher Salt" with our new "Lemon" object. We will declare our lemon
object as a variable for better readibility and include it as the third .splice()
parameter.
const lemon = { "name": "Lemon", "recipe": ["pesto"], "price": 2.04 }
shoppingListArray.splice(6, 1, lemon)
Today I'm feeling a bit saucy so let's change things up a bit and add in some roasted tomatoes using .push(). With .push()
we can add elements to the end of the array, with each parameter representing a new element. So let's add some "Cherry Tomatoes" to our list. Come to think of it, I forgot the "Garlic" too!
const tomatoes = { "name": "Cherry Tomatoes", "recipe": ["pesto"], "price": 5.99 }
const garlic = { "name": "Garlic", "recipe": ["pesto"], "price": 2.99 }
shoppingListArray.push(tomatoes, garlic)
Organizing our shopping list
Now that we have all our ingredients well established let's organize them in a way that will make our shopping experience seamless.
Let's organize our list alphabetically using .sort().
shoppingListArray.sort((a, b) => {
const nameA = a.name
const nameB = b.name
if (nameA < nameB) {
return -1
}
if (nameA > nameB) {
1
}
return 0
})
Our shopping list now looks like this in the console.
Basil
Black Pepper
Cherry Tomatoes
Eggs
Extra Virgin Olive Oil
Garlic
Kosher Salt
Lemon
Parmesan
Pine Nuts
Semolina Flour
Planning ahead for our expected costs
Now we are ready to go to the market, but first let's make sure we know how much money we need, using .reduce(). There is a lot to go over with .reduce()
, and I'm getting hungry, so I'll skip over the details.
const ingredientsPrice = shoppingListArray.reduce((accumulator, ingredient) => {
return accumulator + ingredient.price
}, 0)
console.log("You will need $" + ingredientsPrice + " to make pesto pasta. Man is life is expensive.")
// You will need $98.39 to make pesto pasta. Wow, life is expensive.
Creating new recipe list arrays
So we went to the store and got our ingredients, but now we want to separate our ingredients back into their respective recipes, just to lay everything out on the table and keep things in order. Lets create two new arrays, pastaIngredients
and pestoIngredients
using .filter(), .includes(), and of course .forEach()
to log them to the console.
const pastaIngredients = shoppingListArray.filter((ingredient) => {
return ingredient.recipe.includes('pasta')
})
pastaIngredients.forEach((ingredient) => {
console.log(ingredient.name)
})
const pestoIngredients = shoppingListArray.filter((ingredient) => {
return ingredient.recipe.includes('pesto')
})
pestoIngredients.forEach((ingredient) => {
console.log(ingredient.name)
})
"Wrapping" it up
As you can see from logging these to the console we successfully created a shoppingListArray
that didn't modify our original immutable recipe arrays, pastaRecipeArray
and pestoRecipeArray
. We then were able to mutably modify the shoppingListArray
in a destructive manner to add, delete, and replace ingredients to our liking. We also calculated how much we needed to spend before going to the store. Lastly, we were able to separate these ingredients back into their respective recipes, pastaIngredients
and pestoIngredients
in preparation for a brilliant meal.
Well, what a delicious dish that was. I hope you enjoyed it as much as I did. Again, Mangia Mangia!
Posted on August 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.