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

时间:2020-04-21 10:06:39

标签: javascript arrays object multidimensional-array filter

我有一个像这样的数组:

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"
    }
  ]
};

我想通过我可以用我的产品烹饪的食谱过滤我的食谱数组(在这种情况下,我不能煮“西红柿煎蛋”,因为我没有鸡蛋,也不能煮“土豆泥”,因为我没有足够的土豆)。

到目前为止,我尝试了不同的方法,但并未提出完整的解决方案。

我最接近的解决方案是这样:

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);
  }
};

此名称仅适用于产品名称,不适用于其数量。当我尝试检查金额时,它就坏了。

这是整个代码:

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);

3 个答案:

答案 0 :(得分:2)

我们可以这样做:

  • 使用reduce设置productsObj以允许快速查找
  • 过滤食谱数组
  • 在食谱过滤器函数的每个回调中,并循环遍历每个食谱的成分
  • 对于每种配料,检查其是否存在于productsObj中,并且含量是否大于或等于配方配料中的项目。
  • 如果存在且数量足够,请继续检查其余成分
  • 如果不是,则返回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; }

例如,如果您将产品数量更改为:

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" }
  ],

由于盐和土豆的数量不足,无法获得任何结果。

答案 1 :(得分:1)

您可以使用一个对象来加快访问速度,将amount用作数字以提高可比性

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

只循环一次产品和回执。

此方法使用destructuring assignment,其中属性是从对象中取出的。

它使用对象的值并检查所有成分的可用量。

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);

答案 2 :(得分:0)

您可以尝试此解决方案。

在这里,我添加了一个解决方案,例如,您有<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>Drag</title> </head> <body> <div id="draggable"></div> </body> </html>个单位的“盐” ,并且第一个配方需要20个单位进行烹饪

现在假设,第二个配方需要15单位盐,但是您的商店还有10单位盐。在这种情况下,您不能 第二道菜。

5
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);