Reputation: 8519
I have have map of Object of the following type (example data below)
{[date:string]:Array<{[type:string]:{amount:number, price:number}}>}
And I need to reduce the array to a single object of amount sum and price averaged
I have read a couple of post on how to reduce data over an object like
How to map and reduce over an array of objects?
Javascript reduce on array of objects
In my case the "type" is a ENUM of ["PV","BHKW","FALLBACK"] but it could be extended. Apart from writing declarative for in loops, i can't figure out how to use array.reduce to accomplish this more elegantly.
Any suggestions?
{
"2018-08-01T11:00:00+02:00": [
{
"BHKW": {
"amount": 131,
"price": 85
},
"FALLBACK": {
"amount": 84,
"price": 1
}
},
{
"BHKW": {
"amount": 307,
"price": 58
},
"PV": {
"amount": 4,
"price": 60
}
}
],
"2018-08-01T12:00:00+02:00": [
{
"BHKW": {
"amount": 288,
"price": 59
},
"PV": {
"amount": 742,
"price": 73
}
},
{
"BHKW": {
"amount": 250,
"price": 49
},
"PV": {
"amount": 507,
"price": 98
}
},
{
"PV": {
"amount": 368,
"price": 22
},
"BHKW": {
"amount": 357,
"price": 73
}
},
{
"FALLBACK": {
"amount": 135,
"price": 62
},
"BHKW": {
"amount": 129,
"price": 93
}
}
],
Upvotes: 1
Views: 2470
Reputation: 18525
Another way (although not as elegant as the _.merge
approach already posted) is with _.mapValues/_.reduce/_.assignWith or _.extendWith
:
var data = { "2018-08-01T11:00:00+02:00": [{ "BHKW": { "amount": 131, "price": 85 }, "FALLBACK": { "amount": 84, "price": 1 } }, { "BHKW": { "amount": 307, "price": 58 }, "PV": { "amount": 4, "price": 60 } } ], "2018-08-01T12:00:00+02:00": [{ "BHKW": { "amount": 288, "price": 59 }, "PV": { "amount": 742, "price": 73 } }, { "BHKW": { "amount": 250, "price": 49 }, "PV": { "amount": 507, "price": 98 } }, { "PV": { "amount": 368, "price": 22 }, "BHKW": { "amount": 357, "price": 73 } }, { "FALLBACK": { "amount": 135, "price": 62 }, "BHKW": { "amount": 129, "price": 93 } } ] }
var result =
_.mapValues(data, (x) =>
_.reduce(x, (r, c) =>
_.assignWith(r, c, (o, s) =>
o && s ? ({ amount: o.amount + s.amount, price: o.price + s.price }) : {...o, ...s})
)
)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Upvotes: 0
Reputation: 30098
You can use lodash#mapValues
to transform each value of the object data
object. To combine all objects with the same key values, you can use lodash#mergeWith
with the callback testing if the keys being transformed will be evaluated.
const result = _.mapValues(data, collection =>
_.mergeWith({}, ...collection, (a = 0, b = 0, key) =>
['amount', 'price'].includes(key)
? a + b
: void 0 // is equivalent to undefined to retain current value
));
const data = {
"2018-08-01T11:00:00+02:00": [
{
"BHKW": {
"amount": 131,
"price": 85
},
"FALLBACK": {
"amount": 84,
"price": 1
}
},
{
"BHKW": {
"amount": 307,
"price": 58
},
"PV": {
"amount": 4,
"price": 60
}
}
],
"2018-08-01T12:00:00+02:00": [
{
"BHKW": {
"amount": 288,
"price": 59
},
"PV": {
"amount": 742,
"price": 73
}
},
{
"BHKW": {
"amount": 250,
"price": 49
},
"PV": {
"amount": 507,
"price": 98
}
},
{
"PV": {
"amount": 368,
"price": 22
},
"BHKW": {
"amount": 357,
"price": 73
}
},
{
"FALLBACK": {
"amount": 135,
"price": 62
},
"BHKW": {
"amount": 129,
"price": 93
}
}
]
};
const result = _.mapValues(data, collection =>
_.mergeWith({}, ...collection, (a = 0, b = 0, key) =>
['amount', 'price'].includes(key)
? a + b
: void 0 // is equivalent to undefined to retain current value
));
console.log(result);
.as-console-wrapper{min-height:100%;top:0}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
Upvotes: 2
Reputation: 8519
solved it!
var x = _.reduce(
list,
(acc, item) => {
for (const key in item) {
if (!acc[key]) {
acc[key] = { price: 0, amount: 0 };
}
if (item[key] && item[key].price) {
acc[key].price += item[key].price;
}
if (item[key] && item[key].amount) {
acc[key].amount += item[key].amount;
}
}
return acc;
},
{}
);
Upvotes: 1