How to push a nested array of objects to an array?

Viewed 1681

I have to create and push a nested array of objects to an array.

My code snippet with an array of recipes in the file called recipes.js would look like this:

const createRecipe = () => {
    const id = uuidv4()
 
    recipes.push({
        id: id,
        title: '',
        body: '',
        ingredients: []
    })
    saveRecipe()
 
    return id
}

Then, in the file ingredients.js I imported the function createRecipe() and try to push the ingredients to the recipe array like this:

const createIngredients = (recipes, text) => {
    recipes.ingredients.push({
        id: uuidv4(),
        text,
        completed: false
    })
    saveIngredients()
}

but my code doesn't work, I get an error

what is the right way to push this nested array of ingredients to the recipes array?

4 Answers

As @DarkSigma points out the recipes is an array, which is the source of the error.

To build out on what has already been said, here's some more detail:

recipes.push({
  id: id,
  title: '',
  body: '',
  ingredients: []
})

produces a data structure of:

[{id: 1, title: '', body: '', ingredients: []}, {id: 2, title: '', body: '', ingredients: [] /* etc. */}]

So for each item in recipes, the ingredients is another list of items.

When we get to adding some new ingredients like:

recipes.ingredients.push({
  id: uuidv4(),
  text,
  completed: false
})

the error is recipes.ingredients because recipes is an array so we would have to find the recipe that matched the ingredients we were adding.

Hence, your function would need to do something like:

const createIngredients = (recipes, recipeId , text) => {
    // Look up the recipe for the given id
    const foundRecipeById = recipes.find(x => x.id === recipeId);
    if (foundRecipeById) {
        // Now add the ingredients to the found recipe
        foundRecipeById.ingredients.push({
            id: uuidv4(),
            text,
            completed: false
        })
        saveIngredients()
    }
    else {
        // Do something intelligent if the recipe is not found
    }
}

This way we are looking up/matching the recipe with the ingredients

recipes is an array of objects. Each of those objects has the ingredients field, which is itself an array.

recipesitself does not have an ingredients field.

Therefore, you need to loop through the elements of recipes. For each recipe object, push many ingredients into its ingredients array.

Arrays are lists of objects and these objects can be as complicated as you like. I prefer to create custom objects to hold all of the values that I push onto an array, but that is optional. I do this to ensure that the structure of each entry in the array is exactly the same.

If an object needs to hold an array itself - as in your ingredients for a recipe - you need to build that child array and add it to the main object before adding that main object into the main array (you can add it later, but that's more complicated!).

Here is an example of what I mean:

// create a recipe object
let recipe = {
  id: "",
  name: "",
  ingredients: ""
}
// create an ingredient object with a constructor to make it easier to populate
function ingredient (recipeid, name, quantity){
  this.recipeid = recipeid;
  this.name = name;
  this.quantity = quantity;
}
// create a recipes array and truncate it (it always seems to start with a blank entry)
let recipes = [];
recipes.length = 0;

// Now create a recipe
function addRecipe() {
  let thisrecipe = Object.create(recipe);
  thisrecipe.id = 1;
  thisrecipe.name = "Scrambled eggs";
  // create a dummy array for the ingredients
  let ings = [];
  ings.length = 0;
  // create a new ingredient object for each ingredient
  // and push that onto the dummy array
  let ing = new ingredient(thisrecipe.id, "eggs", "2");
  ings.push(ing);
  ing = new ingredient(thisrecipe.id, "cream", "6 tbps");
  ings.push(ing);
  ing = new ingredient(thisrecipe.id, "butter", "1 knob");
  ings.push(ing);
  // once all ingredients have been added onto the dummy array, update the recipe object's ingredients property
  thisrecipe.ingredients = ings;
  // finally, push the completed recipe onto the recipes array
  recipes.push(thisrecipe);
}

addRecipe();
console.log(recipes);

Obviously, this is all hard coded for one recipe and three ingredients - but, hopefully, it should give you the principles you need to build up a complete recipes array

I think that the push() function is operating on the property and not its value. If you add in a few extra lines:

const createIngredients = (recipes, recipeId , text) => {
    // Look up the recipe for the given id
    const foundRecipeById = recipes.find(x => x.id === recipeId);
    if (foundRecipeById) {
        // Now add the ingredients to the found recipe
        let ings = foundRecipeById.ingredients;
        ings.push({
            id: uuidv4(),
            text,
            completed: false
        })
        foundRecipeById.ingredients = ings;
        saveIngredients()
    }
    else {
        // Do something intelligent if the recipe is not found
    }
}
Related