14

I have an array of objects like below for example.

{name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0}
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}
{name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1}
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
{name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0}
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
{name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}

I want to group them up and adding up all the values in it. For example, the final would be like this:

{name: "Mc Donald", quantity: 8, maleCount: 1, femaleCount: 1}
{name: "KFC", quantity: 54, maleCount: 3, femaleCount: 3}

How can I achieve this in JavaScript?

I have tried to find some solution online but it is not the one I wanted. For example this solution

8 Answers 8

15

You can use array reduce:

var arr = [
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
];

var finalArr = arr.reduce((m, o) => {
    var found = m.find(p => p.name === o.name);
    if (found) {
        found.quantity += o.quantity;
        found.maleCount += o.maleCount;
        found.femaleCount += o.femaleCount;
    } else {
        m.push(o);
    }
    return m;
}, []);

console.log(finalArr);

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

1 Comment

Just noting that this solution will modify some of the original objects.
8

You can use forEach() loop and add to new array. You can pass empty object as thisArg parameter that you can use as this in your callback function and in second forEach context of this will still be the same as in first callback function because of arrow function.

var data = [{"name":"Mc Donald","quantity":4,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"Mc Donald","quantity":4,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":1,"femaleCount":0},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1},{"name":"KFC","quantity":9,"maleCount":0,"femaleCount":1}]
var result = [], keys = ['quantity', 'maleCount', 'femaleCount']

data.forEach(function(e) {
  if(!this[e.name]) result.push(this[e.name] = e);
  else keys.forEach(k => this[e.name][k] += e[k])
}, {})

console.log(result)

1 Comment

Using this like that needs some more explanation, please.
7

You could group with a hash table and use an array for the keys with variable values.

var array = [{ name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }],
    groups = Object.create(null),
    result = array.reduce(function (r, o) {
        var values = ['quantity', 'maleCount', 'femaleCount'];
        if (!groups[o.name]) {
            groups[o.name] = { name: o.name };
            r.push(groups[o.name]);
            values.forEach(function (k) { groups[o.name][k] = 0; });
        }
        values.forEach(function (k) { groups[o.name][k] += o[k]; });
        return r;
    }, []);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

With linq.js

var array = [{ name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }, { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }],
    result = Enumerable.From(array)
        .GroupBy(
            "$.name",
            null,
            "{ name: $.Key, quantity: $$.Sum('$.quantity'), maleCount: $$.Sum('$.maleCount'), femaleCount: $$.Sum('$.femaleCount') }"
        )
        .ToArray();

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/linq.js/2.2.0.2/linq.js"></script>

2 Comments

Is there a reason to do groups = Object.create(null) instead of simply groups = {}?
if you have a name like toString, you could get wrong results with prototypes.
2

My solution based on reduce:

let data = [
  {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
];

// We will only keep an array of values of the merged object
let result = Object.values(
  // Merge data to name indexed objects
  data.reduce((p,c) => {
    // Ensure key (name) exists
    if (!p[c.name]) {
       // If first occurence, duplicate object (to avoid modification of original array)
       p[c.name] = Object.assign({}, c);
    } else {
       // If key (name) already exists, sum up relevant attributes
       p[c.name].quantity += c.quantity;
       p[c.name].maleCount += c.maleCount;
       p[c.name].femaleCount += c.femaleCount;
    }
    // return updated object
    return p;
  }, {})
);

console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

hope this will help

Comments

1

More functional/universal approach

const normalizeValue = (value) => value === undefined ? 0 : value
const sumUpProps = (props = []) => (a = {}, b = {}) => {
  const result = {}
  
  props.forEach((prop) => {
    result[prop] = normalizeValue(a[prop]) + normalizeValue(b[prop])
  })

  return result
}

const data = [
  {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
  {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1}
]

const entries = {}
const sumObjects = sumUpProps(['quantity', 'maleCount', 'femaleCount'])

data.forEach((item) => {
  const entry = entries[item.name] || {}

  entries[item.name] = sumObjects(entry, item)
})


console.log(entries)

Comments

1

check this code below:

var array = [
    {name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
    {name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1},
];

var result = [];

for(var index in array){
    var obj = array[index];
    if(result.hasOwnProperty(obj.name)) {
        result[obj.name].quantity += obj.quantity;
        result[obj.name].maleCount += obj.maleCount;
        result[obj.name].femaleCount += obj.femaleCount;
    }else{
        result[obj.name] = {};
        result[obj.name].quantity = obj.quantity;
        result[obj.name].maleCount = obj.maleCount;
        result[obj.name].femaleCount = obj.femaleCount;
    }
}

console.log(result);

Comments

1

Here is a generic solution that uses clean and modern JavaScript:

function groupRestaurants(restaurants) {
    const groups = new Map();
    restaurants.forEach(restaurant => {
        const group = findOrCreateGroup(groups, restaurant.name);
        addRestaurantToGroup(group, restaurant);
    });
    return [...groups.values()];
}

function findOrCreateGroup(groups, name) {
    let group = groups.get(name);
    if (!group) {
        group = { name };
        groups.set(name, group);
    }
    return group;
}

function addRestaurantToGroup(group, restaurant) {
    for (const [key, value] of Object.entries(restaurant)) {
        if (key !== 'name') {
            if (group.hasOwnProperty(key)) {
                group[key] += value;
            } else {
                group[key] = value;
            }
        }
    }
}

const restaurants = [
    { name: "Mc Donald", quantity: 4, maleCount: 1, femaleCount: 0 },
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 },
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 },
    { name: "Mc Donald", quantity: 4, maleCount: 0, femaleCount: 1 },
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 },
    { name: "KFC", quantity: 9, maleCount: 1, femaleCount: 0 },
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 },
    { name: "KFC", quantity: 9, maleCount: 0, femaleCount: 1 }
];

console.log(groupRestaurants(restaurants));

Comments

1

Lodash makes your life easy.

_.map(_.groupBy(yourArrayHere, 'name'), (groupMembers, groupKey) => {
    let sumObject = {name: groupKey};

    _.forEach(['quantity', 'maleCount', 'femaleCount'], (property) => {
        sumObject[property] = 0;
        _.forEach(groupMembers, (member) => {
            sumObject[property] += member[property];
        });
    });

    return sumObject;
});

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.