按数组中的对象分组最有效的方法是什么?
例如,给定此对象数组:
[
{ 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,然后遍历生成的对象,自己计算总数?
从@mortb、@jmarceli的回答和这篇文章中,
我利用JSON.stringify()作为分组依据的PRIMITIVE VALUE多列的标识。
无第三方
function groupBy(list, keyGetter) {
const map = new Map();
list.forEach((item) => {
const key = keyGetter(item);
if (!map.has(key)) {
map.set(key, [item]);
} else {
map.get(key).push(item);
}
});
return map;
}
const pets = [
{type:"Dog", age: 3, name:"Spot"},
{type:"Cat", age: 3, name:"Tiger"},
{type:"Dog", age: 4, name:"Rover"},
{type:"Cat", age: 3, name:"Leo"}
];
const grouped = groupBy(pets,
pet => JSON.stringify({ type: pet.type, age: pet.age }));
console.log(grouped);
使用Lodash第三方
const pets = [
{type:"Dog", age: 3, name:"Spot"},
{type:"Cat", age: 3, name:"Tiger"},
{type:"Dog", age: 4, name:"Rover"},
{type:"Cat", age: 3, name:"Leo"}
];
let rslt = _.groupBy(pets, pet => JSON.stringify(
{ type: pet.type, age: pet.age }));
console.log(rslt);
如果您需要通过以下方式进行多组:
const populate = (entireObj, keys, item) => {
let keysClone = [...keys],
currentKey = keysClone.shift();
if (keysClone.length > 0) {
entireObj[item[currentKey]] = entireObj[item[currentKey]] || {}
populate(entireObj[item[currentKey]], keysClone, item);
} else {
(entireObj[item[currentKey]] = entireObj[item[currentKey]] || []).push(item);
}
}
export const groupBy = (list, key) => {
return list.reduce(function (rv, x) {
if (typeof key === 'string') (rv[x[key]] = rv[x[key]] || []).push(x);
if (typeof key === 'object' && key.length) populate(rv, key, x);
return rv;
}, {});
}
const myPets = [
{name: 'yaya', type: 'cat', color: 'gray'},
{name: 'bingbang', type: 'cat', color: 'sliver'},
{name: 'junior-bingbang', type: 'cat', color: 'sliver'},
{name: 'jindou', type: 'cat', color: 'golden'},
{name: 'dahuzi', type: 'dog', color: 'brown'},
];
// run
groupBy(myPets, ['type', 'color']));
// you will get object like:
const afterGroupBy = {
"cat": {
"gray": [
{
"name": "yaya",
"type": "cat",
"color": "gray"
}
],
"sliver": [
{
"name": "bingbang",
"type": "cat",
"color": "sliver"
},
{
"name": "junior-bingbang",
"type": "cat",
"color": "sliver"
}
],
"golden": [
{
"name": "jindou",
"type": "cat",
"color": "golden"
}
]
},
"dog": {
"brown": [
{
"name": "dahuzi",
"type": "dog",
"color": "brown"
}
]
}
};