I can't seem to get my head around the array.reduce() function. I've got the following array:
orders = [{
"id": 4930,
"status": "pending",
"line_items": [
{
"item_name": "Crepe",
"item_qty": 2,
"recipe": [
{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
}
]
},
{
"item_name": "Pancake",
"item_qty": 3,
"recipe": [
{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "10",
"ing_unit": "g"
}
]
}
]
},
{
"id": 4927,
"status": "pending",
"line_items": [
{
"item_name": "Pancake",
"item_qty": 2,
"recipe": [
{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "10",
"ing_unit": "g"
}
]
}
]
}];
and I'm trying to get a result like below. I'm not sure how to go about multiplying the ingredient amounts with the item quantities:
"total_ingredients": [{
"ing_name": "Flour",
"ing_qty": "840",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "700",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "14",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "20",
"ing_unit": "g"
}
];
I've tried following the structure that was given here but the arrow function is throwing me off. Any sort of help would be greatly appreciated.
CodePudding user response:
Read about reduce. It's awesome. About this one, let me explain.
We start with this skeleton, preparing to group by some property:
orders.reduce(function(agg, order) {
agg[order["some_property"]] = order;
return agg;
}, {})
That's the idea!
Anyway, for each of these, we iterate the list of recipes and products, with the idea to collect instead of "some_property" the actual "ing_name". While we group, we calculate the total. Finally, Object.values() will remove the keys we grouped by and turn it into the required array.
var orders = [{
"id": 4930,
"status": "pending",
"line_items": [{
"item_name": "Crepe",
"item_qty": 2,
"recipe": [{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
}
]
},
{
"item_name": "Pancake",
"item_qty": 3,
"recipe": [{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "10",
"ing_unit": "g"
}
]
}
]
},
{
"id": 4927,
"status": "pending",
"line_items": [{
"item_name": "Pancake",
"item_qty": 2,
"recipe": [{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "10",
"ing_unit": "g"
}
]
}]
}
];
total_ingredients =
Object.values(orders.reduce(function(agg, order) {
order.line_items.forEach(function(item) {
item.recipe.forEach(function(product) {
agg[product.ing_name] = agg[product.ing_name] || {
ing_name: product.ing_name,
ing_qty: 0,
ing_unit: product.ing_unit
}
agg[product.ing_name].ing_qty = Number(agg[product.ing_name].ing_qty) Number(product.ing_qty)
})
})
return agg;
}, {}))
console.log({
total_ingredients: total_ingredients
})
CodePudding user response:
Thank you to @IT Goldman and @NinaScholz for the prompt responses.
I'm sure there's probably a more elegant way of solving this, but I've come up with something that works.
var orders = [{
"id": 4930,
"status": "pending",
"line_items": [{
"item_name": "Crepe",
"item_qty": 2,
"recipe": [{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
}
]
},
{
"item_name": "Pancake",
"item_qty": 3,
"recipe": [{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "10",
"ing_unit": "g"
}
]
}
]
},
{
"id": 4927,
"status": "pending",
"line_items": [{
"item_name": "Pancake",
"item_qty": 2,
"recipe": [{
"ing_name": "Flour",
"ing_qty": "120",
"ing_unit": "g"
},
{
"ing_name": "Milk",
"ing_qty": "100",
"ing_unit": "ml"
},
{
"ing_name": "Egg",
"ing_qty": "2",
"ing_unit": "each"
},
{
"ing_name": "Sugar",
"ing_qty": "10",
"ing_unit": "g"
}
]
}]
}
];
function sumIngredients(orders) {
total_ingredients = Object.values(orders.reduce(function(agg, order) {
// for each order do the following..
order.line_items.forEach(function(lineitem) {
// for each line item consolidate each item
agg[lineitem.item_name] = agg[lineitem.item_name] || {
item_name: agg[lineitem.item_name] == null ? lineitem.item_name : agg['item_name'] ', ' lineitem.item_name,
item_qty: 0
}
// and their quantities
agg[lineitem.item_name].item_qty = Number(agg[lineitem.item_name].item_qty) Number(lineitem.item_qty);
lineitem.recipe.forEach(function(ingredient) {
// for each ingredient in a line item, here's the new structure
agg[ingredient.ing_name] = agg[ingredient.ing_name] || {
ing_name: ingredient.ing_name,
ing_qty: 0,
ing_unit: ingredient.ing_unit
}
// consolidate ingredient quantities and multiply them with the line-item quantities
agg[ingredient.ing_name].ing_qty = Number(agg[ingredient.ing_name].ing_qty) Number(ingredient.ing_qty * Number(lineitem.item_qty))
})
})
return agg;
}, {}));
return total_ingredients;
};
var orders_arr = sumIngredients(orders);
console.log(orders_arr);
Improvements/suggestions are most welcome :)