按数组中的对象分组最有效的方法是什么?
例如,给定此对象数组:
[
{ 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,然后遍历生成的对象,自己计算总数?
如果您需要通过以下方式进行多组:
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"
}
]
}
};
var arr = [
{ Phase: "Phase 1", `enter code here`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" }
];
创建并清空对象。循环遍历arr并添加使用Phase作为obj的唯一键。在循环遍历arr时,保持更新obj中的键总数。
const obj = {};
arr.forEach((item) => {
obj[item.Phase] = obj[item.Phase] ? obj[item.Phase] +
parseInt(item.Value) : parseInt(item.Value);
});
结果如下:
{ "Phase 1": 50, "Phase 2": 130 }
循环通过obj形成表单和resultArr。
const resultArr = [];
for (item in obj) {
resultArr.push({ Phase: item, Value: obj[item] });
}
console.log(resultArr);
基于@Ceasar Bautista的原始想法,我修改了代码并使用typescript创建了一个groupBy函数。
static groupBy(data: any[], comparator: (v1: any, v2: any) => boolean, onDublicate: (uniqueRow: any, dublicateRow: any) => void) {
return data.reduce(function (reducedRows, currentlyReducedRow) {
let processedRow = reducedRows.find(searchedRow => comparator(searchedRow, currentlyReducedRow));
if (processedRow) {
// currentlyReducedRow is a dublicateRow when processedRow is not null.
onDublicate(processedRow, currentlyReducedRow)
} else {
// currentlyReducedRow is unique and must be pushed in the reducedRows collection.
reducedRows.push(currentlyReducedRow);
}
return reducedRows;
}, []);
};
此函数接受一个回调(比较器)和一个第二个回调(onDuplicate),该回调比较行并查找副本。
用法示例:
data = [
{ name: 'a', value: 10 },
{ name: 'a', value: 11 },
{ name: 'a', value: 12 },
{ name: 'b', value: 20 },
{ name: 'b', value: 1 }
]
private static demoComparator = (v1: any, v2: any) => {
return v1['name'] === v2['name'];
}
private static demoOnDublicate = (uniqueRow, dublicateRow) => {
uniqueRow['value'] += dublicateRow['value'];
};
使命感
groupBy(data, demoComparator, demoOnDublicate)
将执行计算值和的分组。
{name: "a", value: 33}
{name: "b", value: 21}
我们可以根据项目的需要创建任意多个回调函数,并根据需要聚合这些值。在一个例子中,我需要合并两个数组,而不是求和数据。
使用ES6的简单解决方案:
该方法有一个返回模型,可以比较n个财产。
const compareKey = (item, key, compareItem) => {
return item[key] === compareItem[key]
}
const handleCountingRelatedItems = (listItems, modelCallback, compareKeyCallback) => {
return listItems.reduce((previousValue, currentValue) => {
if (Array.isArray(previousValue)) {
const foundIndex = previousValue.findIndex(item => compareKeyCallback(item, currentValue))
if (foundIndex > -1) {
const count = previousValue[foundIndex].count + 1
previousValue[foundIndex] = modelCallback(currentValue, count)
return previousValue
}
return [...previousValue, modelCallback(currentValue, 1)]
}
if (compareKeyCallback(previousValue, currentValue)) {
return [modelCallback(currentValue, 2)]
}
return [modelCallback(previousValue, 1), modelCallback(currentValue, 1)]
})
}
const itemList = [
{ type: 'production', human_readable: 'Production' },
{ type: 'test', human_readable: 'Testing' },
{ type: 'production', human_readable: 'Production' }
]
const model = (currentParam, count) => ({
label: currentParam.human_readable,
type: currentParam.type,
count
})
const compareParameter = (item, compareValue) => {
const isTypeEqual = compareKey(item, 'type', compareValue)
return isTypeEqual
}
const result = handleCountingRelatedItems(itemList, model, compareParameter)
console.log('Result: \n', result)
/** Result:
[
{ label: 'Production', type: 'production', count: 2 },
{ label: 'Testing', type: 'testing', count: 1 }
]
*/