2
\$\begingroup\$

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&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 = () => {
  RECIPES.forEach(recipe => {
    let recipeName = recipe.name;

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

printRecipe();

\$\endgroup\$

migrated from stackoverflow.com Apr 15 '18 at 19:49

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ Looks good. To optimize it you would need a different datastructure, however this sounds like premature optimization. \$\endgroup\$ – Jonas Wilms Apr 15 '18 at 19:48
4
\$\begingroup\$

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&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 => [o.id, o.name]));
const printRecipe = () => {
  RECIPES.forEach(recipe => {
    let listOfItems = recipe.rules.map(
        ingredient => keyedIngredients.get(ingredient)
    ).join(" and ");
    console.log(`${recipe.name} - ${listOfItems}`);
  });
};

printRecipe();

\$\endgroup\$
3
\$\begingroup\$

Convert the INGREDIENTS into a Map (or object), that way you can access ingredients directly, rather than using find. Also, have the reducer return an array that is then turned into a string, rather than checking, formatting, and re-returning the string each time:

const INGREDIENTS = [
  {
    id: 'chocolate',
    name: 'Chocolate Ice Cream',
    type: 'ice_cream',
    contains: ['cream', 'milk_chocolate']
  },
  {
    id: 'm_and_ms',
    name: 'M&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 ingredientsMap = new Map(INGREDIENTS.map(ingredient => [ingredient.id, ingredient]))

const printRecipe = () => {
  RECIPES.forEach(({ name, rules }) => {
    const listOfItems = rules.reduce((acc, currentIngredient, index) => {
      const name = ingredientsMap.get(currentIngredient).name;
      acc.push(name);
      return acc;
    }, []);
    console.log(`${name} - ${listOfItems.join(' AND ')}`);
  });
};

printRecipe();

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.