-3

What is the most efficient way to groupby objects in an array?

For example:

[{
    "month": "October",
    "active_users": 20,
    "invited_users": 35
},
{
    "month": "October",
    "active_users": 50,
    "invited_users": 60
},
{
    "month": "September",
    "active_users": 10,
    "invited_users": 45
},
{
    "month": "September",
    "active_users": 80,
    "invited_users": 95
}]

I want to group similar objects based on the 'month' key, I have taken the reference from here for grouping the similar objects but I want to do some further calculations.

I tried with this:

const data = [{
    "month": "October",
    "active_users": 20,
    "invited_users": 35
},
{
    "month": "October",
    "active_users": 50,
    "invited_users": 60
},
{
    "month": "September",
    "active_users": 10,
    "invited_users": 45
},
{
    "month": "September",
    "active_users": 80,
    "invited_users": 95
}]
function groupBy(objectArray, property) {
   return objectArray.reduce((acc, obj) => {
      const key = obj[property];
      if (!acc[key]) {
         acc[key] = [];
      }
      // Add object to list for given key's value
      acc[key].push(obj);
      return acc;
   }, {});
}
const groupedData = groupBy(data, 'month');
console.log(groupedData);

My expected result is:

[{
  "month": "October",
  "active_users": 70,
  "invited_users": 95
},
{
  "month": "September",
  "active_users": 90,
  "invited_users": 140
}
]

I want to add remaining values of keys of similar objects, what is the most efficient way to achieve this?

Suraj Pawar
  • 227
  • 1
  • 4
  • 15
  • 2
    What have you tried yourself? Something with the [reduce()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce) function maybe? – Mark Baijens Oct 07 '21 at 12:41
  • 1
    Does this answer your question? [How to group an array of objects by key](https://stackoverflow.com/questions/40774697/how-to-group-an-array-of-objects-by-key) – ziishaned Oct 07 '21 at 12:42
  • Yes I tried with reduce() – Suraj Pawar Oct 07 '21 at 12:42
  • 2
    Then show us your code so we can guide you what you did wrong and learn from it. It will be much more beneficial then copy pasting code. – Mark Baijens Oct 07 '21 at 12:43
  • @ziishaned that's output right now I am getting but I want to make new array with only two objects which will contain the object by similar key and values of other keys will be added – Suraj Pawar Oct 07 '21 at 12:44
  • @SurajPawar can you please share expected output – ziishaned Oct 07 '21 at 12:46

3 Answers3

1

You were almost there, except your reduce used an empty object as a start value whereas your expected result is an array, and you missed on adding if an element with the given group key already exists.

Please note that this solution only requires a single iteration over the array (as opposed to other answers). I'm adding this because you asked what is the most efficient way to achieve this?.

const data = [{"month": "October","active_users": 20,"invited_users": 35},{"month": "October","active_users": 50,"invited_users": 60},{"month": "September","active_users": 10,"invited_users": 45},{"month": "September",    "active_users": 80,"invited_users": 95}];

function groupBy(objectArray, property) {
   return objectArray.reduce((acc, obj) => {
      const existingGroup = acc.find(a => a[property] === obj[property]);
      if (!existingGroup) acc.push(obj);
      else {
        for (const [k, v] of Object.entries(obj)) {
          if (typeof v === 'number') {
            existingGroup[k] += v;
          }
        }
      }
      return acc;
   }, []);
}
const groupedData = groupBy(data, 'month');
console.log(groupedData);
connexo
  • 53,704
  • 14
  • 91
  • 128
0

I created this solution for you

const arrayOfObjects = [
  {
    month: "October",
    active_users: 20,
    invited_users: 35
  },
  {
    month: "October",
    active_users: 50,
    invited_users: 60
  },
  {
    month: "September",
    active_users: 10,
    invited_users: 45
  },
  {
    month: "September",
    active_users: 80,
    invited_users: 95
  },
];

const checkIfObjectExists = (objectArray, key, value) => {
  let index;
  objectArray.forEach((element, i) => {
    if (element[key] === value) {
      index = i;
    }
  });
  return index;
};

const result = arrayOfObjects.reduce((prevValue, currentValue) => {
  const value = Array.isArray(prevValue) ? prevValue : [prevValue];
  const index = checkIfObjectExists(value, "month", currentValue.month);
  if (index) {
    value[index].active_users += currentValue.active_users;
    value[index].invited_users += currentValue.invited_users;
    return value;
  }
  value.push(currentValue);
  return value;
}, Object.create(null)).slice(1);

EkkoKo
  • 214
  • 1
  • 5
0

Your current solution only groups the objects based on the property, but not sum the active_users and invited_users for each group. You can turn the your groupBy() return value into an array using Object.entries(). Then map() each group into the desired result by summing the desired properties.

const data = [
  { "month": "October",   "active_users": 20, "invited_users": 35 },
  { "month": "October",   "active_users": 50, "invited_users": 60 },
  { "month": "September", "active_users": 10, "invited_users": 45 },
  { "month": "September", "active_users": 80, "invited_users": 95 },
];

const result = Object.entries(groupBy(data, "month"))
  .map(([month, items]) => {
    const sum = { month, active_users: 0, invited_users: 0 };
    for (const item of items) {
      sum.active_users  += item.active_users;
      sum.invited_users += item.invited_users;
    }
    return sum;
  });

console.log(result);


function groupBy(objectArray, property) {
   return objectArray.reduce((acc, obj) => {
      const key = obj[property];
      if (!acc[key]) {
         acc[key] = [];
      }
      // Add object to list for given key's value
      acc[key].push(obj);
      return acc;
   }, {});
}
3limin4t0r
  • 19,353
  • 2
  • 31
  • 52