按数组中的对象分组最有效的方法是什么?
例如,给定此对象数组:
[
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
{ Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
{ Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]
我正在表格中显示这些信息。我想通过不同的方法进行分组,但我想对值求和。
我将Undercore.js用于其groupby函数,这很有用,但并不能完成全部任务,因为我不希望它们“拆分”,而是“合并”,更像SQL groupby方法。
我要找的是能够合计特定值(如果需要)。
因此,如果我按阶段分组,我希望收到:
[
{ Phase: "Phase 1", Value: 50 },
{ Phase: "Phase 2", Value: 130 }
]
如果我组了阶段/步骤,我会收到:
[
{ Phase: "Phase 1", Step: "Step 1", Value: 15 },
{ Phase: "Phase 1", Step: "Step 2", Value: 35 },
{ Phase: "Phase 2", Step: "Step 1", Value: 55 },
{ Phase: "Phase 2", Step: "Step 2", Value: 75 }
]
是否有一个有用的脚本,或者我应该坚持使用Undercore.js,然后遍历生成的对象,自己计算总数?
这是一个基于TS的功能,不是性能最好的,但很容易阅读和理解!
function groupBy<T>(array: T[], key: string): Record<string, T[]> {
const groupedObject = {}
for (const item of array) {
const value = item[key]
if (groupedObject[value] === undefined) {
groupedObject[value] = []
}
groupedObject[value].push(item)
}
return groupedObject
}
我们以->
const data = [
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
{ Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
];
console.log(groupBy(data, 'Step'))
{
'Step 1': [
{
Phase: 'Phase 1',
Step: 'Step 1',
Task: 'Task 1',
Value: '5'
},
{
Phase: 'Phase 1',
Step: 'Step 1',
Task: 'Task 2',
Value: '10'
}
],
'Step 2': [
{
Phase: 'Phase 1',
Step: 'Step 2',
Task: 'Task 1',
Value: '15'
},
{
Phase: 'Phase 1',
Step: 'Step 2',
Task: 'Task 2',
Value: '20'
}
]
}
我已经改进了答案。此函数获取组字段数组并返回分组对象,该对象的键也是组字段的对象。
function(xs, groupFields) {
groupFields = [].concat(groupFields);
return xs.reduce(function(rv, x) {
let groupKey = groupFields.reduce((keyObject, field) => {
keyObject[field] = x[field];
return keyObject;
}, {});
(rv[JSON.stringify(groupKey)] = rv[JSON.stringify(groupKey)] || []).push(x);
return rv;
}, {});
}
let x = [
{
"id":1,
"multimedia":false,
"language":["tr"]
},
{
"id":2,
"multimedia":false,
"language":["fr"]
},
{
"id":3,
"multimedia":true,
"language":["tr"]
},
{
"id":4,
"multimedia":false,
"language":[]
},
{
"id":5,
"multimedia":false,
"language":["tr"]
},
{
"id":6,
"multimedia":false,
"language":["tr"]
},
{
"id":7,
"multimedia":false,
"language":["tr","fr"]
}
]
groupBy(x, ['multimedia','language'])
//{
//{"multimedia":false,"language":["tr"]}: Array(3),
//{"multimedia":false,"language":["fr"]}: Array(1),
//{"multimedia":true,"language":["tr"]}: Array(1),
//{"multimedia":false,"language":[]}: Array(1),
//{"multimedia":false,"language":["tr","fr"]}: Array(1)
//}
let x = [
{
"id": "6",
"name": "SMD L13",
"equipmentType": {
"id": "1",
"name": "SMD"
}
},
{
"id": "7",
"name": "SMD L15",
"equipmentType": {
"id": "1",
"name": "SMD"
}
},
{
"id": "2",
"name": "SMD L1",
"equipmentType": {
"id": "1",
"name": "SMD"
}
}
];
function groupBy(array, property) {
return array.reduce((accumulator, current) => {
const object_property = current[property];
delete current[property]
let classified_element = accumulator.find(x => x.id === object_property.id);
let other_elements = accumulator.filter(x => x.id !== object_property.id);
if (classified_element) {
classified_element.children.push(current)
} else {
classified_element = {
...object_property,
'children': [current]
}
}
return [classified_element, ...other_elements];
}, [])
}
console.log( groupBy(x, 'equipmentType') )
/* output
[
{
"id": "1",
"name": "SMD",
"children": [
{
"id": "6",
"name": "SMD L13"
},
{
"id": "7",
"name": "SMD L15"
},
{
"id": "2",
"name": "SMD L1"
}
]
}
]
*/