简体   繁体   English

通过使用 object - JavaScript 检查另一个嵌套数组来过滤带有对象的嵌套数组

[英]Filtering a nested array with objects by checking another nested array with object - JavaScript

I have an array like this:我有一个这样的数组:

const state = {
  products: [
    { name: "Potato", amount: "3"},
    { name: "Butter", amount: "1000" },
    { name: "Salt", amount: "2000" },
    //{name: "Egg", amount: "10"},
    { name: "Tomato",  amount: "5"},
    { name: "Sour Milk", amount: "5"}
  ],

  recipes: [
    {
      name: "Mashed potatoes",
      ingredients: [
        { name: "Potato", amount: "5"},
        { name: "Butter", amount: "30"},
        { name: "Salt", amount: "15"}
      ],
      instructions: "Some Text"
    },
    {
      name: "Tomato Omelette",
      ingredients: [
        { name: "Tomato", amount: "1" },
        { name: "Egg", amount: "1" },
        { name: "Salt", amount: "10" },
        { name: "Butter", amount: "40" }
      ],
      instructions: "Some text"
    }
  ]
};

I want to filter my recipes array by recipes that I can cook with my products (in this case I can't cook "Tomato omelette" because I don't have any eggs and I can't cook "Mashed Potatoes" because I don't have enough potatoes) .我想按我可以用我的产品烹饪的食谱过滤我的食谱数组(在这种情况下,我不能做“番茄煎蛋”,因为我没有鸡蛋,也不能做“土豆泥”,因为我没有没有足够的土豆)

So far I tried different approaches but I didn't come up with a whole solution.到目前为止,我尝试了不同的方法,但我没有想出一个完整的解决方案。

My closest solution was this:我最接近的解决方案是:

const filterRecipes = (filter, products, recipes) => {

  if(filter === 'available products') {
      //Getting all product names for future tests
      const productsNames = products.map(item => item.name);

      //Here we will filter all our recipes by available products 
      const result = recipes.filter(recipe => {
          //Getting all ingredient names of the current recipe
          const ingredientNames = recipe.ingredients.map(item => item.name);

          //If we have all products for our recipe
          //we will add it to our filtered array
          if (ingredientNames.every((name) => productsNames.includes(name))){
              return true;
          }
      })

      console.log(result);
  }
};

This one works only for the names of the products but not for its amount.这仅适用于产品名称,但不适用于其数量。 When I'm trying to check for the amount it just brokes.当我试图检查它刚刚打破的金额时。

Here is the whole code:这是整个代码:

 const state = { products: [ { name: "Potato", amount: "5"}, { name: "Butter", amount: "1000" }, { name: "Salt", amount: "2000" }, //{name: "Egg", amount: "10"}, { name: "Tomato", amount: "5"}, { name: "Sour Milk", amount: "5"} ], recipes: [ { name: "Mashed potatoes", ingredients: [ { name: "Potato", amount: "5"}, { name: "Butter", amount: "30"}, { name: "Salt", amount: "15"} ], instructions: "Some Text" }, { name: "Tomato Omelette", ingredients: [ { name: "Tomato", amount: "1" }, { name: "Egg", amount: "1" }, { name: "Salt", amount: "10" }, { name: "Butter", amount: "40" } ], instructions: "Some text" } ] }; const filterRecipes = (filter, products, recipes) => { if(filter === 'available products') { //Getting all product names for future tests const productsNames = products.map(item => item.name); //Here we will filter all our recipes by available products const result = recipes.filter(recipe => { //Getting all ingredient names of the current recipe const ingredientNames = recipe.ingredients.map(item => item.name); //If we have all products for our recipe //we will add it to our filtered array if (ingredientNames.every((name) => productsNames.includes(name))){ return true; } }) console.log(result); } }; filterRecipes("available products", state.products, state.recipes);

We can do it like this:我们可以这样做:

  • Set up a productsObj with reduce to allow fast lookups使用 reduce 设置productsObj以允许快速查找
  • filter the recipes array过滤食谱数组
  • inside each callback of the recipes filter function, and loop over ingredients of each recipe在食谱过滤器 function 的每个回调中,并循环每个食谱的成分
  • for each ingredient, check it exists in productsObj and the amount is larger than or equal to the item in the recipe ingredients.对于每种成分,检查它是否存在于productsObj中,并且数量大于或等于配方成分中的项目。
  • if it is present and with large enough qty keep checking the rest of the ingredients如果它存在并且数量足够大,请继续检查成分的 rest
  • if not, return false - ie filter out of array.如果不是,则返回 false - 即过滤出数组。

 const state = { products: [ { name: "Potato", amount: "5" }, { name: "Butter", amount: "1000" }, { name: "Salt", amount: "2000" }, { name: "Egg", amount: "10" }, { name: "Tomato", amount: "5" }, { name: "Sour Milk", amount: "5" } ], recipes: [ { name: "Mashed potatoes", ingredients: [ { name: "Potato", amount: "5" }, { name: "Butter", amount: "30" }, { name: "Salt", amount: "15" } ], instructions: "Some Text" }, { name: "Tomato Omelette", ingredients: [ { name: "Tomato", amount: "1" }, { name: "Egg", amount: "1" }, { name: "Salt", amount: "10" }, { name: "Butter", amount: "40" } ], instructions: "Some text" } ] }; const filterRecipes = (filter, products, recipes) => { if (filter === "available products") { //Getting all product names in an object for fast look-up const productsObj = products.reduce((aggObj, item) => { aggObj[item.name] = item; return aggObj; }, {}); //console.log("o", productsObj); //Here we will filter all our recipes by available products const result = recipes.filter((recipe) => { let valid = true; //true until proven false //Loop over ingredients of each recipe for (let i = 0; i < recipe.ingredients.length; i++) { const item = recipe.ingredients[i]; const lookup = productsObj[item.name] || false; const quantityEnough = lookup? parseInt(lookup.amount) >= parseInt(item.amount): false; if (;quantityEnough) { valid = false; break; } } return valid; }). console;log(result); } }, filterRecipes("available products". state,products. state;recipes);
 .as-console-wrapper { max-height: 100%;important: top; 0; }

For example if you change your product quantitiess to:例如,如果您将产品数量更改为:

const state = {
  products: [
    { name: "Potato", amount: "4" },
    { name: "Butter", amount: "1000" },
    { name: "Salt", amount: "2" },
    { name: "Egg", amount: "10" },
    { name: "Tomato", amount: "5" },
    { name: "Sour Milk", amount: "5" }
  ],

You get no results as salt and potatoes quantities are not large enough for either recipe.你没有得到任何结果,因为盐和土豆的数量对于任何一种食谱都不够大。

You could take an object for faster access, with amount as numbers for better comparability您可以使用 object 以获得更快的访问速度, amount为数字以获得更好的可比性

{
    Potato: 5,
    Butter: 1000,
    Salt: 2000,
    Tomato: 5,
    "Sour Milk": 5
}

and loop only products and recieps once.并且只循环产品和收据一次。

This approach uses destructuring assignment , where properties are taken out of objects.这种方法使用解构赋值,其中属性从对象中取出。

The it uses the value of the object and checks the available amount for all ingredients.它使用 object 的值并检查所有成分的可用量。

 const filter = ({ products, recipes }) => { const avalilable = products.reduce((r, { name, amount }) => (r[name] = +amount, r), {}); console.log(avalilable ) return recipes.filter(({ ingredients }) => ingredients.every(({ name, amount }) => avalilable[name] >= +amount)); }, state = { products: [{ name: "Potato", amount: "5" }, { name: "Butter", amount: "1000" }, { name: "Salt", amount: "2000" }, { name: "Tomato", amount: "5" }, { name: "Sour Milk", amount: "5" }], recipes: [{ name: "Mashed potatoes", ingredients: [{ name: "Potato", amount: "5" }, { name: "Butter", amount: "30" }, { name: "Salt", amount: "15" }], instructions: "Some Text" }, { name: "Tomato Omelette", ingredients: [{ name: "Tomato", amount: "1" }, { name: "Egg", amount: "1" }, { name: "Salt", amount: "10" }, { name: "Butter", amount: "40" }], instructions: "Some text" }] }, recipes = filter(state); console.log(recipes);

You can try this solution.你可以试试这个解决方案。

Here I add a solution to a situation say, you have 20 units of "salt" and the first recipe takes 15 units of them for cooking.在这里,我添加了一个解决方案,比如你有20单位的“盐” ,第一个食谱需要15个单位的盐来烹饪。

Now assume, the second recipe needs 10 units of salt but you have 5 units left at your store.现在假设,第二个配方需要10单位的盐,但您的商店还剩下5单位。 In this situation, you cannot take the second recipe for cooking.在这种情况下,您不能采用第二种烹饪方法。

 const state = { products: [ { name: "Potato", amount: "5"}, { name: "Butter", amount: "1000" }, { name: "Salt", amount: "20" }, { name: "Egg", amount: "1"}, { name: "Tomato", amount: "5"}, { name: "Sour Milk", amount: "5"} ], recipes: [ { name: "Mashed potatoes", ingredients: [ { name: "Potato", amount: "5"}, { name: "Butter", amount: "30"}, { name: "Salt", amount: "15"} ], instructions: "Some Text" }, { name: "Tomato Omelette", ingredients: [ { name: "Tomato", amount: "1" }, { name: "Egg", amount: "1" }, { name: "Salt", amount: "10" }, { name: "Butter", amount: "40" } ], instructions: "Some text" } ] }; const filterRecipes = (filter, products, recipes) => { if (filter === 'available products') { /** * Restructure the products from array to object. * like {Potato: "20", "Salt": "200"} */ const store = products.reduce((a, {name, amount}) => { return {...a, [name]: amount}; }, {}); const canCook = recipes.filter(recipe => { /** * Convert ingredient from array to object like products * */ const ingredients = recipe.ingredients.reduce((a, {name, amount}) => { return {...a, [name]: amount}; }, {}); /** * Check if every ingredients are available at the store */ const allow = Object.entries(ingredients).every(([name, amount]) => { return (store[name];== undefined && (+store[name]) >= (+amount)); }). /** * This is for reducing the amount of ingredient from the store * if the recipe is taken for cooking. * You can omit it if you don't need to measure this factor. */ if (allow) { Object.entries(ingredients),forEach(([name; amount]) => { store[name] -= amount; }); } return allow; }). console;log(canCook), } } filterRecipes("available products". state,products. state;recipes);
 .as-console-wrapper {min-height: 100%;important: top; 0;}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM