biggbest
biggbest

Reputation: 628

Sum array of objects with Lodash

I'd like to sum objects from array, I've been searching and testing different things founds around there, using Lodash or not, without any success.

Here is the data array, there is 5 elements but it could be less or more. The properties will always be the same, but there could be a lot more.

const data = [
    {
        from: "2019-10-15",
        stats: [
            {
                options: {
                    width: 15,
                    height: 20,
                    borders: 35,
                    removable: 5
                }
            }
        ]
    },
    {
        from: "2019-10-16",
        stats: [
            {
                options: {
                    width: 22,
                    height: 18,
                    borders: 10,
                    removable: 0
                }
            }
        ]
    },
    {
        from: "2019-10-17",
        stats: [
            {
                options: {
                    width: 0,
                    height: 15,
                    borders: 15,
                    removable: 0
                }
            }
        ]
    },
    {
        from: "2019-10-18",
        stats: [
            {
                options: {
                    width: 20,
                    height: 20,
                    borders: 10,
                    removable: 5,
                }
            }
        ]
    },
    {
        from: "2019-10-19",
        stats: [
            {
                options: {
                    width: 0,
                    height: 10,
                    borders: 0,
                    removable: 30
                }
            }
        ]
    }
];

The expected result is the sum of each array element stats[0].options properties:

const sum = {
    width: 57,
    height: 83,
    borders: 70,
    removable: 40
}

I know it's definitely not complicated.

Upvotes: 0

Views: 849

Answers (3)

Guillermo Moratorio
Guillermo Moratorio

Reputation: 662

Another approach to do this with a reduce and nested forEach, but a bit more straightforward:

const data = [
    {
        from: "2019-10-15",
        stats: [
            {
                options: {
                    width: 15,
                    height: 20,
                    borders: 35,
                    removable: 5
                }
            }
        ]
    },
    {
        from: "2019-10-16",
        stats: [
            {
                options: {
                    width: 22,
                    height: 18,
                    borders: 10,
                    removable: 0
                }
            }
        ]
    },
    {
        from: "2019-10-17",
        stats: [
            {
                options: {
                    width: 0,
                    height: 15,
                    borders: 15,
                    removable: 0
                }
            }
        ]
    },
    {
        from: "2019-10-18",
        stats: [
            {
                options: {
                    width: 20,
                    height: 20,
                    borders: 10,
                    removable: 5,
                }
            }
        ]
    },
    {
        from: "2019-10-19",
        stats: [
            {
                options: {
                    width: 0,
                    height: 10,
                    borders: 0,
                    removable: 30
                }
            }
        ]
    }
];

let result = _.reduce(data, (acc, value) =>{
      // our nested options object
      const options = value.stats[0].options;
      
      _.forEach(options, (optionValue, optionKey) =>{
        // if we know about this key already, then add optionValue to it
        // if not, this will be our first value for that key
        acc[optionKey] = !!acc[optionKey] ? acc[optionKey] + optionValue : optionValue;
      })
      
      return acc;
}, {})

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

Upvotes: 0

Ori Drori
Ori Drori

Reputation: 191936

Use _.map() to get the options, then combine the objects using _.mergeWith(), and use _.add() as the customizer.

const data = [{"from":"2019-10-15","stats":[{"options":{"width":15,"height":20,"borders":35,"removable":5}}]},{"from":"2019-10-16","stats":[{"options":{"width":22,"height":18,"borders":10,"removable":0}}]},{"from":"2019-10-17","stats":[{"options":{"width":0,"height":15,"borders":15,"removable":0}}]},{"from":"2019-10-18","stats":[{"options":{"width":20,"height":20,"borders":10,"removable":5}}]},{"from":"2019-10-19","stats":[{"options":{"width":0,"height":10,"borders":0,"removable":30}}]}];

const result = _.mergeWith({}, ..._.map(data, 'stats[0].options'), _.add);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>

If you use lodash/fp you can create a function using _.flow(), and replace _.mergeWith with _.mergeAllWith():

const { flow, map, mergeAllWith, add } = _;

const fn = flow(
  map('stats[0].options'),
  mergeAllWith(add)
);

const data = [{"from":"2019-10-15","stats":[{"options":{"width":15,"height":20,"borders":35,"removable":5}}]},{"from":"2019-10-16","stats":[{"options":{"width":22,"height":18,"borders":10,"removable":0}}]},{"from":"2019-10-17","stats":[{"options":{"width":0,"height":15,"borders":15,"removable":0}}]},{"from":"2019-10-18","stats":[{"options":{"width":20,"height":20,"borders":10,"removable":5}}]},{"from":"2019-10-19","stats":[{"options":{"width":0,"height":10,"borders":0,"removable":30}}]}];

const result = fn(data);

console.log(result);
<script src='https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)'></script>

Upvotes: 3

StepUp
StepUp

Reputation: 38094

It can be done through vanill JavaScript. Just use reduce and foreach methods:

const data = [
    {
        from: "2019-10-15",
        stats: [
            {
                options: {
                    width: 15,
                    height: 20,
                    borders: 35,
                    removable: 5
                }
            }
        ]
    },
    {
        from: "2019-10-16",
        stats: [
            {
                options: {
                    width: 22,
                    height: 18,
                    borders: 10,
                    removable: 0
                }
            }
        ]
    },
    {
        from: "2019-10-17",
        stats: [
            {
                options: {
                    width: 0,
                    height: 15,
                    borders: 15,
                    removable: 0
                }
            }
        ]
    },
    {
        from: "2019-10-18",
        stats: [
            {
                options: {
                    width: 20,
                    height: 20,
                    borders: 10,
                    removable: 5,
                }
            }
        ]
    },
    {
        from: "2019-10-19",
        stats: [
            {
                options: {
                    width: 0,
                    height: 10,
                    borders: 0,
                    removable: 30
                }
            }
        ]
    }
];

const result = data.reduce((a, {stats}) => {
    stats.forEach(({options}) => {
        for (const key in options) {
            a[key] = a[key] || 0;
            a[key] += options[key];
        }
    });

    return a;
}, {})

console.log(result);

The vanilla JS code looks like this:

const result = data.reduce((a, {stats}) => {
    stats.forEach(({options}) => {
        for (const key in options) {
            a[key] = a[key] || 0;
            a[key] += options[key];
        }
    });

    return a;
}, {})

Upvotes: 2

Related Questions