Home > OS >  Looping through and reducing nested array
Looping through and reducing nested array

Time:07-25

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

  • Related