按数组中的对象分组最有效的方法是什么?
例如,给定此对象数组:
[
{ 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'
}
]
}
为了补充Scott Sauyet的答案,一些人在评论中询问如何使用他的函数按值1、值2等分组,而不是仅对一个值分组。
只需编辑他的求和函数:
DataGrouper.register("sum", function(item) {
return _.extend({}, item.key,
{VALUE1: _.reduce(item.vals, function(memo, node) {
return memo + Number(node.VALUE1);}, 0)},
{VALUE2: _.reduce(item.vals, function(memo, node) {
return memo + Number(node.VALUE2);}, 0)}
);
});
保持主组(DataGrouper)不变:
var DataGrouper = (function() {
var has = function(obj, target) {
return _.any(obj, function(value) {
return _.isEqual(value, target);
});
};
var keys = function(data, names) {
return _.reduce(data, function(memo, item) {
var key = _.pick(item, names);
if (!has(memo, key)) {
memo.push(key);
}
return memo;
}, []);
};
var group = function(data, names) {
var stems = keys(data, names);
return _.map(stems, function(stem) {
return {
key: stem,
vals:_.map(_.where(data, stem), function(item) {
return _.omit(item, names);
})
};
});
};
group.register = function(name, converter) {
return group[name] = function(data, names) {
return _.map(group(data, names), converter);
};
};
return group;
}());
如果您需要通过以下方式进行多组:
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"
}
]
}
};