I'm trying to find an efficient way to group and sum data from an object array in javascript. I am open to using the lodash as a library for this, and have researched other similar questions, but not exactly what I'm looking for. Here I would like to create a generic function that takes an array of data, an array of fields to group by and an array of fields to sum.
Take this example object array:
const data = [
{
continent:'North America',
country:'us',
state:'ma',
population: 1567986,
fee: 24343
},
{
continent:'North America',
country:'us',
state:'tx',
population: 7567986,
fee: 119343
},
{
continent:'North America',
country:'ca',
state:'on',
population: 467986,
fee: 3343
},
{
continent:'North America',
country:'ca',
state:'qb',
population: 1268986,
fee: 54248
},
{
continent:'Europe',
country:'ge',
state:'mc',
population: 4268986,
fee: 97333
},
{
continent:'Europe',
country:'it',
state:'fl',
population: 978353,
fee: 248
},
]
I want to create a function that looks like:
const GroupByFunction = (array, groupbyFields, sumFields) {
}
if I called it with say these fields const result1 = GroupByFuntion(data,['continent'],['population','fee'])
the result would look like this where popSum and popFee are just made up attribute names to hold the summed values:
const result1 = [
{
contintent:'North America',
popSum:10872944,
popfee:201277,
data: [
{
continent:'North America',
country:'us',
state:'ma',
population: 1567986,
fee: 24343
},
{
continent:'North America',
country:'us',
state:'tx',
population: 7567986,
fee: 119343
},
{
continent:'North America',
country:'ca',
state:'on',
population: 467986,
fee: 3343
},
{
continent:'North America',
country:'ca',
state:'qb',
population: 1268986,
fee: 54248
},
]
},
{
continent:'Europe',
popSum: 5247339,
feeSum:97581,
data: [
{
continent:'Europe',
country:'ge',
state:'mc',
population: 4268986,
fee: 97333
},
{
continent:'Europe',
country:'it',
state:'fl',
population: 978353,
fee: 248
},
]
}
]
But, I could also pass in multiple group by keys like this:
const result2 = GroupByFuntion(data,['continent','country'],['population','fee'])
and the result would look like this (the popSum and popFee are just junk data placeholders, they would be the summed values from the array):
const result2 = [
{
continent:'North America',
popSum:10872944,
popfee:201277,
country: [
{
country:'us',
popSum: 2423423,
popFee: 2323,
data: [
{
continent:'North America',
country:'us',
state:'ma',
population: 1567986,
fee: 24343
},
{
continent:'North America',
country:'us',
state:'tx',
population: 7567986,
fee: 119343
},
]
},
{
country:'ca',
popSum: 54443,
popFee: 34324,
data: [
{
continent:'North America',
country:'ca',
state:'on',
population: 467986,
fee: 3343
},
{
continent:'North America',
country:'ca',
state:'qb',
population: 1268986,
fee: 54248
},
]
}
]
},
{
continent:'Europe',
popSum: 3432423,
popFee: 3432,
country: [
{
country: 'gb',
popSum: 32432,
popFee: 564,
data: [
{
continent:'Europe',
country:'ge',
state:'mc',
population: 4268986,
fee: 97333
},
]
},
{
country: 'it',
popSum: 89332,
popFee: 8932,
data: [
{
continent:'Europe',
country:'ge',
state:'mc',
population: 4268986,
fee: 97333
},
]
},
]
},
]
Is this possible using the _groupBy and _sumBy functions in lodash, or some other way?
CodePudding user response:
This seems to achieve what you want :
const data = [
{
continent:'North America',
country:'us',
state:'ma',
population: 1567986,
fee: 24343
},
{
continent:'North America',
country:'us',
state:'tx',
population: 7567986,
fee: 119343
},
{
continent:'North America',
country:'ca',
state:'on',
population: 467986,
fee: 3343
},
{
continent:'North America',
country:'ca',
state:'qb',
population: 1268986,
fee: 54248
},
{
continent:'Europe',
country:'ge',
state:'mc',
population: 4268986,
fee: 97333
},
{
continent:'Europe',
country:'it',
state:'fl',
population: 978353,
fee: 248
},
]
const groupBy = (rawArray, groupbyFields, sumFields) => {
const field = groupbyFields[0]
const fieldValues = new Set()
rawArray.forEach(e => fieldValues.add(e[field]))
const array = []
for (const fieldValue of fieldValues.values()) {
let data = rawArray.filter(e => e[field] === fieldValue)
if (groupbyFields.length > 1) {
data = groupBy(data, groupbyFields.slice(1), sumFields)
}
const obj = { data }
sumFields.forEach(sumField => {
const sumFieldName = `${ sumField }Sum`
obj[sumFieldName] = data.map(e => e[sumField] ?? e[sumFieldName] ?? 0).reduce((g, c) => g c, 0)
})
obj[field] = fieldValue
array.push(obj)
}
return array
}
console.log(JSON.stringify(groupBy(data, [ 'continent', 'country' ],[ 'population', 'fee' ]), null, ' '))