[英]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_type
和cost_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_type
, cost_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.