[英]Group and aggregate array of objects by key names
我想在JS中編寫一個函數,該函數將名稱列表作為參數,並且能夠按指定的列名稱進行分組和匯總。 例如,我的數據可能如下所示:
const SALES = [
{ lead: 'Mgr 1', revenue: 49.99, repName: 'Rep 1', forecast: 81.00 },
{ lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 1', forecast: 91.00 },
{ lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 13', forecast: 82.00 },
{ lead: 'Mgr 2', revenue: 99.99, repName: 'Rep 3', forecast: 101.00 },
{ lead: 'Mgr 2', revenue: 9.99, repName: 'Rep 5', forecast: 89.00 },
{ lead: 'Mgr 3', revenue: 199.99, repName: 'Rep 6', forecast: 77.00 }
];
我可以像這樣對數據進行分組和匯總:
let grouped = {};
SALES.forEach(({lead, repName, revenue}) => {
grouped[[lead, repName]] = grouped[[lead, repName]] || { lead, repName, revenue: 0 };
grouped[[lead, repName]].revenue = +grouped[[lead, repName]].revenue + (+revenue);
});
grouped = Object.values(grouped);
console.warn('Look at this:\n', grouped);
但是,我希望它更具動態性,這樣我就不必為分組和聚合的所有可能組合編寫if-else語句。 以下代碼顯示了一些我想開始使用的東西,但目前還沒有。
function groupByTotal(arr, groupByCols, aggregateCols) {
let grouped = {};
arr.forEach(({ groupByCols, aggregateCols }) => {
grouped[groupByCols] = grouped[groupByCols] || { groupByCols, aggregateCols: 0 };
grouped[groupByCols].aggregateCols = +grouped[groupByCols].aggregateCols + (+aggregateCols);
});
grouped = Object.values(grouped);
return grouped;
}
groupByTotal(SALES,['lead','repName'],'revenue')
預期的輸出可能如下所示:
[
{ lead: "Mgr 1", repName: "Rep 1", revenue: 59.98 },
{ lead: "Mgr 1", repName: "Rep 13", revenue: 9.99 },
{ lead: "Mgr 2", repName: "Rep 3", revenue: 99.99 },
{ lead: "Mgr 2", repName: "Rep 5", revenue: 9.99 },
{ lead: "Mgr 3", repName: "Rep 6", revenue: 199.99 }
]
理想情況下,我希望能夠傳入任意數量的列名以進行分組或聚合。 任何幫助將不勝感激。
當前,您正在基於[lead, repName]
的字符串化值創建密鑰。 您可以基於groupByCols
動態groupByCols
// gets the values for "groupByCols" seperated by `|` to create a unique key
const values = groupByCols.map(k => o[k]).join("|");
您還需要基於groupByCols
獲取對象的子集
const subSet = (o, keys) => keys.reduce((r, k) => (r[k] = o[k], r), {})
// OR if fromEntries() is supported
const subSet = (o, keys) => Object.fromEntries(keys.map(k => [k, o[k]))
其余邏輯將類似於您已經在做的事情。 在grouped
使用唯一性。 獲取對象的子集,並根據鍵是否已經存在來添加/更新aggregateCols
鍵
const SALES = [ { lead: 'Mgr 1', revenue: 49.99, repName: 'Rep 1', forecast: 81.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 1', forecast: 91.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 13', forecast: 82.00 }, { lead: 'Mgr 2', revenue: 99.99, repName: 'Rep 3', forecast: 101.00 }, { lead: 'Mgr 2', revenue: 9.99, repName: 'Rep 5', forecast: 89.00 }, { lead: 'Mgr 3', revenue: 199.99, repName: 'Rep 6', forecast: 77.00 } ]; const subSet = (o, keys) => keys.reduce((r, k) => (r[k] = o[k], r), {}) function groupByTotal(arr, groupByCols, aggregateCols) { let grouped = {}; arr.forEach(o => { const values = groupByCols.map(k => o[k]).join("|"); if (grouped[values]) grouped[values][aggregateCols] += o[aggregateCols] else grouped[values] = { ...subSet(o, groupByCols), [aggregateCols]: o[aggregateCols] } }) return Object.values(grouped); } console.log("Sum revenue based on lead and repName") console.log(groupByTotal(SALES, ['lead', 'repName'], 'revenue')) console.log("Sum forecast based on lead: ") console.log(groupByTotal(SALES, ['lead'], 'forecast'))
如果您想傳遞一個列數組進行求和,則可以遍歷aggregateCols
並對grouped
每個屬性求和:
if (grouped[values]) {
aggregateCols.forEach(col => grouped[values][col] += o[col])
grouped[values].Count++
} else {
grouped[values] = subSet(o, groupByCols);
grouped[values].Count = 1
aggregateCols.forEach(col => grouped[values][col] = o[col])
}
您可以使用純JavaScript實現類似的算法。
只知道如何創建密鑰並聚合數據,如以下情況所示。
const SALES = [ { lead: 'Mgr 1', revenue: 49.99, repName: 'Rep 1', forecast: 81.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 1', forecast: 91.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 13', forecast: 82.00 }, { lead: 'Mgr 2', revenue: 99.99, repName: 'Rep 3', forecast: 101.00 }, { lead: 'Mgr 2', revenue: 9.99, repName: 'Rep 5', forecast: 89.00 }, { lead: 'Mgr 3', revenue: 199.99, repName: 'Rep 6', forecast: 77.00 } ]; console.log(aggregate(SALES, ['lead', 'repName'], 'revenue')); function aggregate(data, keyFields, accumulator) { var createNewObj = (ref, fields) => { return fields.reduce((result, key) => { return Object.assign(result, { [key] : ref[key] }); }, {}); } return Object.values(data.reduce((result, object, index, ref) => { let key = keyFields.map(key => object[key]).join(''); let val = result[key] || createNewObj(object, keyFields); val[accumulator] = (val[accumulator] || 0) + object[accumulator]; return Object.assign(result, { [key] : val }); }, {})); }
.as-console-wrapper { top: 0; max-height: 100% !important; }
[
{
"lead": "Mgr 1",
"repName": "Rep 1",
"revenue": 59.98
},
{
"lead": "Mgr 1",
"repName": "Rep 13",
"revenue": 9.99
},
{
"lead": "Mgr 2",
"repName": "Rep 3",
"revenue": 99.99
},
{
"lead": "Mgr 2",
"repName": "Rep 5",
"revenue": 9.99
},
{
"lead": "Mgr 3",
"repName": "Rep 6",
"revenue": 199.99
}
]
下面的示例使用一個累加器對象,該對象包含一個參考字段和一個應用數學表達式的函數。
{
key: 'revenue',
fn : (total, value) => total + value
}
const SALES = [ { lead: 'Mgr 1', revenue: 49.99, repName: 'Rep 1', forecast: 81.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 1', forecast: 91.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 13', forecast: 82.00 }, { lead: 'Mgr 2', revenue: 99.99, repName: 'Rep 3', forecast: 101.00 }, { lead: 'Mgr 2', revenue: 9.99, repName: 'Rep 5', forecast: 89.00 }, { lead: 'Mgr 3', revenue: 199.99, repName: 'Rep 6', forecast: 77.00 } ]; console.log(aggregate(SALES, ['lead', 'repName'], { key: 'revenue', fn : (total, value) => total + value })); function aggregate(data, keyFields, accumulator) { var createNewObj = (ref, fields) => { return fields.reduce((result, key) => { return Object.assign(result, { [key] : ref[key] }); }, {}); } return Object.values(data.reduce((result, object, index, ref) => { let key = keyFields.map(key => object[key]).join(''); let val = result[key] || createNewObj(object, keyFields); val[accumulator.key] = accumulator.fn(val[accumulator.key] || 0, object[accumulator.key]); return Object.assign(result, { [key] : val }); }, {})); }
.as-console-wrapper { top: 0; max-height: 100% !important; }
如果要累積多個字段,則需要刪除該字段以進行引用,而只是修改整個對象,但這通常更危險。
const SALES = [ { lead: 'Mgr 1', revenue: 49.99, repName: 'Rep 1', forecast: 81.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 1', forecast: 91.00 }, { lead: 'Mgr 1', revenue: 9.99, repName: 'Rep 13', forecast: 82.00 }, { lead: 'Mgr 2', revenue: 99.99, repName: 'Rep 3', forecast: 101.00 }, { lead: 'Mgr 2', revenue: 9.99, repName: 'Rep 5', forecast: 89.00 }, { lead: 'Mgr 3', revenue: 199.99, repName: 'Rep 6', forecast: 77.00 } ]; console.log(aggregate(SALES, ['lead', 'repName'], (prev, curr) => { return Object.assign(prev, { revenueTotal : (prev['revenueTotal'] || 0) + curr['revenue'], forecastMax : Math.max((prev['forecastMax'] || -Number.MAX_VALUE), curr['forecast']), forecastMin : Math.min((prev['forecastMin'] || +Number.MAX_VALUE), curr['forecast']) }); })); function aggregate(data, keyFields, accumulatorFn) { var createNewObj = (ref, fields) => { return fields.reduce((result, key) => { return Object.assign(result, { [key] : ref[key] }); }, {}); } return Object.values(data.reduce((result, object, index, ref) => { let key = keyFields.map(key => object[key]).join(''); let val = result[key] || createNewObj(object, keyFields); return Object.assign(result, { [key] : accumulatorFn(val, object) }); }, {})); }
.as-console-wrapper { top: 0; max-height: 100% !important; }
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.