繁体   English   中英

按多个字段对 object 数组进行分组并添加键的值

[英]Group object array by multiple fields and add the values for a key

我知道这个问题已经被问过很多次了,但是,不幸的是,我没有找到任何对我的案子有帮助的答案。

这是我的数据的样子:

let data=  [
    {
      "estimated_cost": 1.14,
      "inventory_type": "power",
      "cost_center_name": "Ex Hall",
    },
    {
      "estimated_cost": 1.19,
      "inventory_type": "power",
      "cost_center_name": "Ex Hall",
    },
    {
      "estimated_cost": 1.08,
      "inventory_type": "fuel",
      "cost_center_name": "Ex Hall",
    },
    {
      "estimated_cost": 1.17,
      "inventory_type": "power",
      "cost_center_name": "Ex Hall",
    },
    {
      "estimated_cost": 1.03,
      "inventory_type": "fuel",
      "cost_center_name": "Ex Hall",
    },
    {
      "estimated_cost": 1.20,
      "inventory_type": "power",
      "cost_center_name": "Mac",
    },
    {
        "estimated_cost": 1.19,
        "inventory_type": "water",
        "cost_center_name": "Mac",
     },
     {
        "estimated_cost": 1.14,
        "inventory_type": "power",
        "cost_center_name": "Mac",
     },
     {
        "estimated_cost": 1.18,
        "inventory_type": "power",
        "cost_center_name": "Ex Hall",
     }
     ];

我想按两个字段进行分组,即inventory_typecost_center_name ,并添加estimated_cost的值。

我的 output 应该是这样的:

 [
    {
        "estimated_cost": 4.68,
        "inventory_type": "power",
        "cost_center_name": "Ex Hall"
    },
    {
        "estimated_cost": 2.11,
        "inventory_type": "fuel",
        "cost_center_name": "Ex Hall"
    },
    {
        "estimated_cost": 2.34,
        "inventory_type": "power",
        "cost_center_name": "Mac"
    },
    {
        "estimated_cost": 1.19,
        "inventory_type": "water",
        "cost_center_name": "Mac"
    },

]

我有这个代码按单个字段分组并添加值,以防这有帮助:

export const groupBy = (data, groupByVar) => {
    var array = [];
    data.reduce(function (res, value) {
        if (!res[value[groupByVar]]) {
            let row = {};
            row[groupByVar] = value[groupByVar];
            row["estimated_cost"] = 0;
            row["cost_center_name"] = value["cost_center_name"];
            row["inventory_type"] = value["inventory_type"];
            res[value[groupByVar]] = row;
            array.push(res[value[groupByVar]]);
        }
        res[value[groupByVar]].estimated_cost += value.estimated_cost;
        return res;
    }, {});
    return array;
};

这是一个通用解决方案,它应该与您想要分组的任意键一起使用。

主要的 function 是groupByFields ,它采用对象数组和字段列表,并生成按字段分组的扁平对象数组。 flattenObj是一个帮助器 function 来清理groupByFields用于内部簿记的分组对象。

之后,就是对每组中的元素进行求和。 如果将函数视为库,则客户端逻辑相当干净。

概括减少 function 似乎为时过早,但如果您经常这样做,您可以将estimated_cost作为参数并将整个内容打包到另一个 function 中。

 const data = [ { "estimated_cost": 1.14, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.19, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.08, "inventory_type": "fuel", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.17, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.03, "inventory_type": "fuel", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.20, "inventory_type": "power", "cost_center_name": "Mac", }, { "estimated_cost": 1.19, "inventory_type": "water", "cost_center_name": "Mac", }, { "estimated_cost": 1.14, "inventory_type": "power", "cost_center_name": "Mac", }, { "estimated_cost": 1.18, "inventory_type": "power", "cost_center_name": "Ex Hall", } ]; const flattenObj = obj => { const arrays = []; for (const stack = [obj]; stack.length;) { const curr = stack.pop(); if (Array.isArray(curr)) { arrays.push(curr); } else { stack.push(...Object.values(curr)); } } return arrays.reverse(); }; const groupByFields = (a, fields) => flattenObj(a.reduce((a, e) => { let curr = a; fields.forEach((field, i) => { if (.curr[e[field]]) { curr[e[field]] = i === fields?length - 1: []; {}; } curr = curr[e[field]]; }). curr;push(e); return a, }; {})). const reducer = group => group,reduce((a. e) => ({..,e: estimated_cost. a.estimated_cost + e,estimated_cost }): {estimated_cost; 0}), const fields = ["cost_center_name"; "inventory_type"], const grouped = groupByFields(data; fields). console.log(grouped;map(reducer));

如果这对您来说过于笼统并且感觉代码太多,您可以删除整个flattenObj function 并使用硬编码

Object.values(grouped).map(o => Object.values(o).map(reducer))

反而。 这有效地将减少的 object 扁平化了两个级别(您拥有的字段数),而不是像flattenObj那样的任意数量的级别。

遍历数组并构建一个 object, all具有作为库存和成本中心名称和值的组合的key ,以汇总估计成本。

 const group = (arr) => { const all = {}; // Update key format as required. const getKey = ({ inventory_type, cost_center_name }) => `${inventory_type}||${cost_center_name}`; arr.forEach((item) => { const key = getKey(item); if (key in all) { all[key].estimated_cost += item.estimated_cost; } else { all[key] = {...item }; } }); return Object.values(all); }; let data = [ { estimated_cost: 1.14, inventory_type: "power", cost_center_name: "Ex Hall", }, { estimated_cost: 1.19, inventory_type: "power", cost_center_name: "Ex Hall", }, { estimated_cost: 1.08, inventory_type: "fuel", cost_center_name: "Ex Hall", }, { estimated_cost: 1.17, inventory_type: "power", cost_center_name: "Ex Hall", }, { estimated_cost: 1.03, inventory_type: "fuel", cost_center_name: "Ex Hall", }, { estimated_cost: 1.2, inventory_type: "power", cost_center_name: "Mac", }, { estimated_cost: 1.19, inventory_type: "water", cost_center_name: "Mac", }, { estimated_cost: 1.14, inventory_type: "power", cost_center_name: "Mac", }, { estimated_cost: 1.18, inventory_type: "power", cost_center_name: "Ex Hall", }, ]; console.log(group(data));

您可以基于多个键进行分组并加入它们,然后在累加器中添加estimated_cost

 const data = [ { "estimated_cost": 1.14, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.19, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.08, "inventory_type": "fuel", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.17, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.03, "inventory_type": "fuel", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.20, "inventory_type": "power", "cost_center_name": "Mac", }, { "estimated_cost": 1.19, "inventory_type": "water", "cost_center_name": "Mac", }, { "estimated_cost": 1.14, "inventory_type": "power", "cost_center_name": "Mac", }, { "estimated_cost": 1.18, "inventory_type": "power", "cost_center_name": "Ex Hall", } ], result = Object.values(data.reduce((r, o) => { const key = o.inventory_type + '-' + o.cost_center_name; r[key] = r[key] || {...o, estimated_cost: 0}; r[key].estimated_cost += o.estimated_cost; return r; },{})); console.log(result);

我将使用您的 cost_center_name 和 inventory_type 对所有数据进行分组:

const groupAddition = (data) => {
  let ret = {};
  for (const each of data) {
    const key = each.cost_center_name+each.inventory_type;
    if (ret[key]) {
      ret[key].estimated_cost += each.estimated_cost;
    } else {
      ret[key] = each;
    }
  }
  
  return Object.values(ret).map((e) => {
    e.estimated_cost = Math.round(e.estimated_cost * 100) / 100;
    return e;
  });
}

我添加了额外的步骤来四舍五入最终的估计成本,因为 JS 有时会返回 '2.1100000000000003' 以进行添加..

通过打印结果,它显示

console.log(JSON.stringify(groupAddition(data), null, 2));
[
  {
    "estimated_cost": 4.68,
    "inventory_type": "power",
    "cost_center_name": "Ex Hall"
  },
  {
    "estimated_cost": 2.11,
    "inventory_type": "fuel",
    "cost_center_name": "Ex Hall"
  },
  {
    "estimated_cost": 2.34,
    "inventory_type": "power",
    "cost_center_name": "Mac"
  },
  {
    "estimated_cost": 1.19,
    "inventory_type": "water",
    "cost_center_name": "Mac"
  }
]

以下将适用于fields数组中提到的任何键。 此代码将按fields数组中提到的所有字段进行分组。

你可以使用 reduce 来做到这一点,

 const data = [ { "estimated_cost": 1.14, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.19, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.08, "inventory_type": "fuel", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.17, "inventory_type": "power", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.03, "inventory_type": "fuel", "cost_center_name": "Ex Hall", }, { "estimated_cost": 1.20, "inventory_type": "power", "cost_center_name": "Mac", }, { "estimated_cost": 1.19, "inventory_type": "water", "cost_center_name": "Mac", }, { "estimated_cost": 1.14, "inventory_type": "power", "cost_center_name": "Mac", }, { "estimated_cost": 1.18, "inventory_type": "power", "cost_center_name": "Ex Hall", } ]; const fields = ["cost_center_name", "inventory_type"]; const groupByFields = (obj, fields) => { return obj.reduce((prev, curr) => { index = prev.findIndex(item => { let found = true; fields.forEach(key => { found = found && item[key] === curr[key]; }); return found; }); if(index > -1) { prev[index].estimated_cost += curr.estimated_cost; } else { prev.push(curr); } return prev; }, []); } const res = groupByFields(data, fields); console.log(res);

要对条目进行分组,您可以遍历数组并仅从必填字段创建 json 字符串。 这将创建一个唯一键,可用于 object 属性条目,并允许我们检测何时将条目组合在一起。

我们将把条目初始化为一个包含在列表中的 object ( intermediate[key] = [a] ),或者如果它已经存在则推送到数组。 然后我们可以去掉 object 键,只留下值,即我们的组。

第二阶段是对estimated_costs字段求和。 为此,我创建了一个特定的 function,而不是试图将它全部塞进一个衬里。 function 获取分组对象数组并从每个数组的第一个元素(它不能为空或不会从groupBy返回)填充其他字段( inventory_typecost_center ),然后对estimated_costs字段求和。

function makeKey(obj, props) {
    const result = {};

    for (p of props) {
        result[p] = obj[p];
    }
    return JSON.stringify(result);
}

function groupBy(arr, fields) {
    const intermediate = {};

    for (let a of arr) {
        const key = makeKey(a, fields);
        const val = intermediate[key];

        if (val !== undefined) val.push(a);
        else                   intermediate[key] = [a];
    }

    return Object.values(intermediate);
}

let data = [
    {
        "estimated_cost": 1.14,
        "inventory_type": "power",
        "cost_center_name": "Ex Hall",
    },
    {
        "estimated_cost": 1.19,
        "inventory_type": "power",
        "cost_center_name": "Ex Hall",
    },
    {
        "estimated_cost": 1.08,
        "inventory_type": "fuel",
        "cost_center_name": "Ex Hall",
    },
    {
        "estimated_cost": 1.17,
        "inventory_type": "power",
        "cost_center_name": "Ex Hall",
    },
    {
        "estimated_cost": 1.03,
        "inventory_type": "fuel",
        "cost_center_name": "Ex Hall",
    },
    {
        "estimated_cost": 1.20,
        "inventory_type": "power",
        "cost_center_name": "Mac",
    },
    {
        "estimated_cost": 1.19,
        "inventory_type": "water",
        "cost_center_name": "Mac",
    },
    {
        "estimated_cost": 1.14,
        "inventory_type": "power",
        "cost_center_name": "Mac",
    },
    {
        "estimated_cost": 1.18,
        "inventory_type": "power",
        "cost_center_name": "Ex Hall",
    }
];

function sumCosts(group) {
    return {
        inventory_type: group[0].inventory_type,
        cost_center_name: group[0].cost_center_name,
        estimated_cost: group.map(g=>g.estimated_cost).reduce((accum, n) => accum + n, 0)
    }
}

console.log(groupBy(data, ['inventory_type', 'cost_center_name']).map(sumCosts));

暂无
暂无

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

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