1

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
            }
        }
    ],

3 Answers 3

2

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>

Sign up to request clarification or add additional context in comments.

Comments

1

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;
    },
    {}
);

Comments

0

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.