Agrupar por e somar usando sublinhado/Lodash
-
21-12-2019 - |
Pergunta
Eu tenho JSON assim:
[
{
platformId: 1,
payout: 15,
numOfPeople: 4
},
{
platformId: 1,
payout: 12,
numOfPeople: 3
},
{
platformId: 2,
payout: 6,
numOfPeople: 5
},
{
platformId: 2,
payout: 10,
numOfPeople: 1
},
]
E eu quero agrupá-lo por platformId
com soma de payout
e numOfPeople
.
Ou sejacomo resultado, quero JSON assim:
[
"1": {
payout: 27,
numOfPeople: 7
},
"2": {
payout: 16,
numOfPeople: 6
}
]
Eu tentei usar underscore.js
de _.groupBy
método e agrupa bem, mas como posso obter a SOMA dos valores das propriedades dos objetos como demonstrei acima?
Solução
Você pode fazer isso sem sublinhado:
var result = data.reduce(function(acc, x) {
var id = acc[x.platformId]
if (id) {
id.payout += x.payout
id.numOfPeople += x.numOfPeople
} else {
acc[x.platformId] = x
delete x.platformId
}
return acc
},{})
Mas por que você iria querer um objeto com teclas numéricas?Você poderia convertê-lo novamente em uma coleção:
var toCollection = function(obj) {
return Object.keys(obj)
.sort(function(x, y){return +x - +y})
.map(function(k){return obj[k]})
}
toCollection(result)
Observe que os objetos sofrem mutação, portanto você pode cloná-los primeiro se quiser manter os dados originais.
Outras dicas
Aqui está um Lodash solução para esse tipo de problema.É semelhante ao Underscore, mas com alguns recursos mais avançados.
const data = [{
platformId: 1,
payout: 15,
numOfPeople: 4
},
{
platformId: 1,
payout: 12,
numOfPeople: 3
},
{
platformId: 2,
payout: 6,
numOfPeople: 5
},
{
platformId: 2,
payout: 10,
numOfPeople: 1
},
];
const ans = _(data)
.groupBy('platformId')
.map((platform, id) => ({
platformId: id,
payout: _.sumBy(platform, 'payout'),
numOfPeople: _.sumBy(platform, 'numOfPeople')
}))
.value()
console.log(ans);
<script src="https://cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script>
Tentei fazer isso da forma funcional, e o resultado ficou assim
console.log(_.chain(data)
.groupBy("platformId")
.map(function(value, key) {
return [key, _.reduce(value, function(result, currentObject) {
return {
payout: result.payout + currentObject.payout,
numOfPeople: result.numOfPeople + currentObject.numOfPeople
}
}, {
payout: 0,
numOfPeople: 0
})];
})
.object()
.value());
No espírito de Lodash One Liners
_.map(_.groupBy(your_list, 'group_by'), (o,idx) => { return { id: idx, summed: _.sumBy(o,'sum_me') }})
Uma maneira funcional (na minha opinião) de usar underscore.js:
var sum = function(t, n) { return t + n; };
_.mapObject(
_.groupBy(data, 'platformId'),
function(values, platformId) {
return {
payout: _.reduce(_.pluck(values, 'payout'), sum, 0),
numOfPeople: _.reduce(_.pluck(values, 'numOfPeople'), sum, 0)
};
}
);
//Input
[{
"platformId": 1,
"payout": 15,
"numOfPeople": 4
}, {
"platformId": 1,
"payout": 12,
"numOfPeople": 3
}, {
"platformId": 2,
"payout": 6,
"numOfPeople": 5
}, {
"platformId": 2,
"payout": 10,
"numOfPeople": 1
}]
//Output
{
"1": {
"payout": 43,
"numOfPeople": 13
},
"2": {
"payout": 43,
"numOfPeople": 13
}
}
// Code
output = _.reduce(input, function(acc, val, key) {
acc[val.platformId] = {
payout: _.sum(a, 'payout'),
numOfPeople: _.sum(a, 'numOfPeople')
};
return acc;
}, {});