Search code examples
javascriptarraysfor-loopobjectnested-loops

JS For Loops Returning Empty Array


The chooseRecipe function should compare the arrays in bakeryA and bakeryB with the ingredients of each recipe. If bakeryA and bakeryB each have an ingredient for a recipe, then the name of the recipe should be printed. In this case, Persian Cheesecake is what should be printed. However, it keeps returning an empty array.

I understand that I'm starting off with an empty array, but shouldn't suitableRecipe.push(recipes[i].name); be taking care of that?

Would appreciate any guidance, or suggestions for a better way to do this.

let bakeryA = ['saffron', 'eggs', 'tomato paste', 'coconut', 'custard'];
let bakeryB = ['milk', 'butter', 'cream cheese'];
let recipes = [
    {
        name: 'Coconut Sponge Cake',
        ingredients: ['coconut', 'cake base']
    },
    {
        name: 'Persian Cheesecake',
        ingredients: ['saffron', 'cream cheese']
    },
    {
        name: 'Custard Surprise',
        ingredients: ['custard', 'ground beef']
    }
];

const chooseRecipe = function(bakeryA, bakeryB, recipes) {
  let suitableRecipe = [];
  for (let i = 0; i < recipes.length; i++) {
    for (let j = 0; j < recipes[i].ingredients.length; j++) {
      for (let k = 0; k < bakeryA.length; k++) {
        if (bakeryA[k] === recipes[i].ingredients[j]) {
          for (let l = 0; l < bakeryB.length; l++) {
            for (let m = 0; m < recipes[i].ingredients; m++) {
              if (bakeryB[l] === recipes[i].ingredients[m]) {
                suitableRecipe.push(recipes[i].name);
              }
            }
          }
        }
      }
    }
  }
  return suitableRecipe;
}

console.log(chooseRecipe(bakeryA, bakeryB, recipes));

Solution

  • There is a much cleaner way to do this, and it involves using Sets. Assuming that bakeryA and bakeryB list each ingredient once:

    let bakeryA = ['saffron', 'eggs', 'tomato paste', 'coconut', 'custard'];
    let bakeryB = ['milk', 'butter', 'cream cheese'];
    let recipes = [
        {
            name: 'Coconut Sponge Cake',
            ingredients: ['coconut', 'cake base']
        },
        {
            name: 'Persian Cheesecake',
            ingredients: ['saffron', 'cream cheese']
        },
        {
            name: 'Custard Surprise',
            ingredients: ['custard', 'ground beef']
        }
    ];
    
    const chooseRecipe = function(bakeryA, bakeryB, recipes) {
      let aIngredients = new Set(bakeryA);
      let bIngredients = new Set(bakeryB);
      return recipes.filter(recipe => 
         recipe.ingredients.every(ingredient =>
             aIngredients.has(ingredient) || bIngredients.has(ingredient))
      );
    }
    
    console.log(chooseRecipe(bakeryA, bakeryB, recipes));

    Some notes:

    • Array.prototype.every allows you to check if every ingredient in a recipe is in either bakeryA or bakeryB.
    • Array.prototype.filter is used to iterate over and filter out values that don't match that condition specified.

    This approach is also more efficient, since it saves you from having to loop 5 (!) times, which will get very very slow as bakeryA, bakeryB, ingredients and recipes become larger and larger.