如何聚合对象数组中的嵌套值

How to aggregate nested values in array of objects

我有一个现有函数,可以从两个数组(一个数组是交易数据,另一个是主数据)创建一个汇总对象。

目前,返回的对象包含按年和按月的交易数量总和-示例如下。

我正在寻求帮助以在根级别为每个项目添加总和 - 下面还有示例。

我查看了类似问题的答案,但未能找到足够接近我的用例的解决方案。

这是现有代码:

const
    array1 = [{ material: "ABC123", cost: 100 },
              { material: "DEF456", cost: 150 }],
    array2 = [{ material: "ABC123", date: "1/1/20", quantity: 4 },
              { material: "ABC123", date: "1/15/20", quantity: 1 },
              { material: "ABC123", date: "2/15/20", quantity: 3 },
              { material: "ABC123", date: "4/15/21", quantity: 1 },
              { material: "DEF456", date: "3/05/20", quantity: 6 },
              { material: "DEF456", date: "3/18/20", quantity: 1 },
              { material: "DEF456", date: "5/15/21", quantity: 2 }],
    groups = [
        ({ material }) => material,
        _ => 'byYear',
        ({ date }) => '20' + date.split('/')[2],
        _ => 'byMonth'
    ],
    sum = {
        key: ({ date }) => date.split('/')[0],
        value: ({ quantity }) => quantity
    }, 
    result = array2.reduce(
        (r, o) => {
            const temp = groups.reduce((t, fn) => t[fn(o)] ??= {}, r);
            temp[sum.key(o)] ??= 0;
            temp[sum.key(o)] += sum.value(o);
            return r;
        },
        Object.fromEntries(array1.map(({ material, cost }) => [material, { cost }]))
    );

console.log(result);

如上所述,我希望它在根级别添加一个“totalSum”key/value。结果如下:

{
"ABC123": {
    "totalSum": 8,
    "cost": 100,
    "byYear": {
        "2020": {
            "byMonth": {
                "1": {
                    "sum": 5,
                    "max": 4
                },
                "2": {
                    "sum": 3,
                    "max": 3
                    }
                }
            }
        }
    }
}

非常感谢任何帮助!

您实际上可以通过 master 数组的单个 Array#reduce() call, combined with a Map 检索 cost.

来实现整个重构

然后添加总和就变得微不足道了,因为您拥有所有可用的属性并且不必重新映射聚合数据。

const
  array1 = [{ material: "ABC123", cost: 100, critical: true }, { material: "DEF456", cost: 150, critical: false }],
  array2 = [{ material: "ABC123", date: "1/1/20", quantity: 4 }, { material: "ABC123", date: "1/15/20", quantity: 1 }, { material: "ABC123", date: "2/15/20", quantity: 3 }, { material: "ABC123", date: "4/15/21", quantity: 1 }, { material: "DEF456", date: "3/05/20", quantity: 6 }, { material: "DEF456", date: "3/18/20", quantity: 1 }, { material: "DEF456", date: "5/15/21", quantity: 2 }],

  master = new Map(array1.map(({ material, ...item }) => [material, item])),
  result = array2.reduce((acc, { material, date, quantity }) => {
    let [month, , year] = date.split('/');
    year = '20' + year;

    const
      _material = (acc[material] ??= { ...master.get(material), totalSum: 0, byYear: {} }),
      _byYear = (_material.byYear[year] ??= { byMonth: {} }),
      _byMonth = (_byYear.byMonth[month] ??= { sum: 0, max: 0 });

    _material.totalSum += quantity;
    _byMonth.sum += quantity;
    _byMonth.max = Math.max(_byMonth.max, quantity);

    return acc;
  }, {});

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

编辑

在您的评论中提到您可能在 array1 中有更多应该包含在最终结果中的属性。我已经通过更改 Map 来引用 array1 中找到的整个对象来进行编辑以适应这一点。当我们第一次在累加器中声明相关对象时,我们可以使用 spread syntax 包含所有可用的属性。

// split out 'material' from the rest of the object in the Map
const master = new Map(array1.map(({ material, ...item }) => [material, item]));

// later access the object by material and spread its props into the accumulator object.
_material = (acc[material] ??= { ...master.get(material), totalSum: 0, byYear: {} }),