Group by and calculate mean / average of properties in a Javascript array
One way to do this is to use reduce
and map
in conjunction.
const myData = [
{team: "GSW", pts: 120, ast: 18, reb: 11},
{team: "GSW", pts: 125, ast: 28, reb: 18},
{team: "GSW", pts: 110, ast: 35, reb: 47},
{team: "HOU", pts: 100, ast: 17, reb: 43},
{team: "HOU", pts: 102, ast: 14, reb: 32},
{team: "SAS", pts: 127, ast: 21, reb: 25},
{team: "SAS", pts: 135, ast: 25, reb: 37},
{team: "SAS", pts: 142, ast: 18, reb: 27}
]
// Calculate the sums and group data (while tracking count)
const reduced = myData.reduce(function(m, d){
if(!m[d.team]){
m[d.team] = {...d, count: 1};
return m;
}
m[d.team].pts += d.pts;
m[d.team].ast += d.ast;
m[d.team].reb += d.reb;
m[d.team].count += 1;
return m;
},{});
// Create new array from grouped data and compute the average
const result = Object.keys(reduced).map(function(k){
const item = reduced[k];
return {
team: item.team,
ast: item.ast/item.count,
pts: item.pts/item.count,
reb: item.reb/item.count
}
})
console.log(JSON.stringify(result,null,4));
EDIT: Just saw your update to the question. You can do away with each line for each key if you can either whitelist (provide an array of keys to compute) or blacklist (provide an array of keys to ignore) keys to do that programmatically.
const myData = [
{team: "GSW", pts: 120, ast: 18, reb: 11},
{team: "GSW", pts: 125, ast: 28, reb: 18},
{team: "GSW", pts: 110, ast: 35, reb: 47},
{team: "HOU", pts: 100, ast: 17, reb: 43},
{team: "HOU", pts: 102, ast: 14, reb: 32},
{team: "SAS", pts: 127, ast: 21, reb: 25},
{team: "SAS", pts: 135, ast: 25, reb: 37},
{team: "SAS", pts: 142, ast: 18, reb: 27}
]
/**
* Function which accepts a data array and a list of whitelisted
* keys to find the average of each key after grouping
*/
function getGroupedData(data, whitelist) {
// Calculate the sums and group data (while tracking count)
const reduced = data.reduce(function(m, d) {
if (!m[d.team]) {
m[d.team] = { ...d,
count: 1
};
return m;
}
whitelist.forEach(function(key) {
m[d.team][key] += d[key];
});
m[d.team].count += 1;
return m;
}, {});
// Create new array from grouped data and compute the average
return Object.keys(reduced).map(function(k) {
const item = reduced[k];
const itemAverage = whitelist.reduce(function(m, key) {
m[key] = item[key] / item.count;
return m;
}, {})
return {
...item, // Preserve any non white-listed keys
...itemAverage // Add computed averege for whitelisted keys
}
})
}
console.log(JSON.stringify(getGroupedData(myData, ['pts', 'ast', 'reb']), null, 4));