Logging the name of recipes using ingredients Given two objects with some food data, log the name of the recipe and ingredients names separated by conjunction "and". Please run the code I have attached to see the expected output.

condition - match RECIPES.rules.id with INGREDIENT.id and use INGREDIENT.name in the final output. 

I was wondering if my solution can be improved? The fact that I am looping array inside an existing array loop makes me think this solution is not good.



const INGREDIENTS = [
  {
    id: 'chocolate',
    name: 'Chocolate Ice Cream',
    type: 'ice_cream',
    contains: ['cream', 'milk_chocolate']
  },
  {
    id: 'm_and_ms',
    name: 'M&amp;Ms',
    type: 'topping',
    contains: ['milk_chocolate']
  },
  {
    id: 'lemon',
    name: 'Lemon Ice Cream',
    type: 'ice_cream',
    contains: ['cream', 'lemon']
  },
  {
    id: 'peanuts',
    name: 'Peanuts',
    type: 'topping',
    contains: ['peanuts']
  },
  {
    id: 'vanilla',
    name: 'Vanilla Ice Cream',
    type: 'ice_cream',
    contains: ['cream', 'vanilla_extract']
  },
  {
    id: 'strawberry',
    name: 'Strawberry Ice Cream',
    type: 'ice_cream',
    contains: ['cream', 'strawberries']
  },
  {
    id: 'caramel',
    name: 'Caramel Sauce',
    type: 'topping',
    contains: ['caramel']
  },
  {
    id: 'raspberry',
    name: 'Raspberry Sauce',
    type: 'topping',
    contains: ['raspberries']
  }
];

// DOCSTRING
const RECIPES = [
  {
    id: 'the_basic',
    name: 'The Basic',
    rules: ['chocolate', 'vanilla']
  },
  {
    id: 'chocolate_lovers',
    name: 'Chocolate Lovers',
    rules: ['chocolate', 'm_and_ms', 'caramel']
  },
  {
    id: 'tooty_fruity',
    name: 'Tooty Fruity',
    rules: ['strawberry', 'raspberry', 'm_and_ms']
  }
];

const printRecipe = () =&gt; {
  RECIPES.forEach(recipe =&gt; {
    let recipeName = recipe.name;

    let listOfItems = recipe.rules.reduce((acc, currentIngredient, index) =&gt; {
      let result;
      let items = INGREDIENTS.find(ingrd =&gt; {
        return ingrd.id === currentIngredient;
      });
      if (acc.length === 0) {
        result = items.name;
      } else {
        result = `${acc} and ${items.name}`;
      }
      return result;
    }, '');
    console.log(`${recipeName} - ${listOfItems}`);
  });
};

printRecipe();
I would suggest two improvements:


Use a Map to have a faster lookup in your ingredients. find needs to iterate through an array every time, while if you create a Map for it, you can get each result in constant time
Use the map array method (unrelated to the above mentioned Map object) in combination with join instead of reduce.


Here is how that would look:



const INGREDIENTS = [{id: 'chocolate',name: 'Chocolate Ice Cream',type: 'ice_cream',contains: ['cream', 'milk_chocolate']},{id: 'm_and_ms',name: 'M&amp;Ms',type: 'topping',contains: ['milk_chocolate']},{id: 'lemon',name: 'Lemon Ice Cream',type: 'ice_cream',contains: ['cream', 'lemon']},{id: 'peanuts',name: 'Peanuts',type: 'topping',contains: ['peanuts']},{id: 'vanilla',name: 'Vanilla Ice Cream',type: 'ice_cream',contains: ['cream', 'vanilla_extract']},{id: 'strawberry',name: 'Strawberry Ice Cream',type: 'ice_cream',contains: ['cream', 'strawberries']},{id: 'caramel',name: 'Caramel Sauce',type: 'topping',contains: ['caramel']},{id: 'raspberry',name: 'Raspberry Sauce',type: 'topping',contains: ['raspberries']}];
const RECIPES = [{id: 'the_basic',name: 'The Basic',rules: ['chocolate', 'vanilla']},{id: 'chocolate_lovers',name: 'Chocolate Lovers',rules: ['chocolate', 'm_and_ms', 'caramel']},{id: 'tooty_fruity',name: 'Tooty Fruity',rules: ['strawberry', 'raspberry', 'm_and_ms']}];

const keyedIngredients = new Map(INGREDIENTS.map(o =&gt; [o.id, o.name]));
const printRecipe = () =&gt; {
  RECIPES.forEach(recipe =&gt; {
    let listOfItems = recipe.rules.map(
        ingredient =&gt; keyedIngredients.get(ingredient)
    ).join(" and ");
    console.log(`${recipe.name} - ${listOfItems}`);
  });
};

printRecipe();