What’s the most efficient way to group by objects in an array in JavaScript and sum specific values?

Hello everyone!!

I’m working with a dataset structured as an array of objects, and I’m looking for the most efficient way to perform a specific type of aggregation.

I have an array of numbers like this:

[
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40" }
]

I need to apply a JavaScript group by operation using different methods and simultaneously sum the Value field. For example, if I group by Phase, I want to get this output:

[
    { Phase: "Phase 1", Value: 50 },
    { Phase: "Phase 2", Value: 130 }
]

And if I group by Phase and Step together, I’d want this result:

[
    { Phase: "Phase 1", Step: "Step 1", Value: 15 },
    { Phase: "Phase 1", Step: "Step 2", Value: 35 },
    { Phase: "Phase 2", Step: "Step 1", Value: 55 },
    { Phase: "Phase 2", Step: "Step 2", Value: 75 }
]

I’m currently using Underscore.js’s groupBy function, but it doesn’t sum the values automatically. Should I stick with it and manually loop through the grouped results, or is there a more efficient way to achieve this grouping and summing directly in JavaScript?

Would really appreciate any insights or code patterns! Thanks for the help!

Hello @keerti_gautam ! Your question about grouping and summing values in a JavaScript array of objects is a common and powerful data manipulation task!

One of the most efficient and idiomatic ways to achieve this in JavaScript is by using the **Array.prototype.reduce** method. This allows you to dynamically group the objects and simultaneously sum up the Value field in a single pass.

Here’s how you can do it for grouping by Phase and Step:

const data = [
  { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: 5 },
  { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: 10 },
  { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: 15 },
  { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: 20 },
  { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: 25 },
  { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: 30 },
  { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: 35 },
  { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: 40 }
];

const result = data.reduce((acc, { Phase, Step, Value }) => {
  const key = `${Phase}-${Step || ''}`.trim(); // Combine Phase and Step for grouping
  if (!acc[key]) {
    acc[key] = { Phase, Step, Value: 0 };
  }
  acc[key].Value += Number(Value); // Add the values together
  return acc;
}, {});

const groupedData = Object.values(result);
console.log(groupedData);

Explanation: The reduce method is used to accumulate results into an object (acc), where the key is a combination of Phase and Step (or just Phase if you adjust the key logic). If the key doesn’t exist yet, it creates a new entry, and the Value is summed using +=. Finally, Object.values() converts the accumulated object back into an array.

This approach is highly efficient for both grouping and summing.

That’s it! Hope this helps! Happy Coding :grin:

Hello @keerti_gautam and @tim-khorev!!

Adding another powerful technique to the conversation about grouping and summing array objects, following the reduce method suggestion.

If you’re comfortable using modern JavaScript features, you can utilize a Map for Grouping and Summing very efficiently. This is especially useful when you want more control over the resulting data structure, and a Map often gives you better performance when dealing with large datasets.

Here’s how you can approach it:

const data = [
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: 5 },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: 10 },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: 15 },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: 20 },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: 25 },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: 30 },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: 35 },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: 40 }
];

const grouped = new Map();

data.forEach(({ Phase, Step, Value }) => {
    const key = `${Phase}-${Step || ''}`;
    if (!grouped.has(key)) {
        grouped.set(key, { Phase, Step, Value: 0 });
    }
    grouped.get(key).Value += Number(Value);
});

const groupedData = Array.from(grouped.values());
console.log(groupedData);

Explanation:

  • A Map is used to store the grouped data, where the key for each entry is based on a combination of Phase and Step.
  • If a key doesn’t exist yet, a new entry is created.
  • The Value field for each corresponding group is summed up.
  • Finally, Array.from() is used to convert the map’s values back into your desired array of objects.

Hope you find this useful!! See ya :–)

Hello @keerti_gautam. I will tell you about one more solution in the discussion on grouping and summing array objects, especially since you mentioned your familiarity with Underscore.js.

Since you’re already familiar with Underscore.js, you can still use its **groupBy method** combined with map to handle both grouping and summing. This approach allows you to group by Phase, and then easily iterate over each group to sum the Value.

Here’s an example for grouping by Phase:

const data = [
  { Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: 5 },
  { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: 10 },
  { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: 15 },
  { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: 20 },
  { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: 25 },
  { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: 30 },
  { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: 35 },
  { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: 40 }
];

const grouped = _.groupBy(data, 'Phase');
const summed = _.map(grouped, (group, Phase) => {
  const totalValue = _.reduce(group, (sum, obj) => sum + Number(obj.Value), 0);
  return { Phase, Value: totalValue };
});

console.log(summed);

Explanation:

  • groupBy is used to group the data by Phase.
  • map is then used to iterate over each group and sum the Value field using reduce.
  • The result is an array of objects with summed values by phase.

Hope this familiar approach helps you efficiently aggregate your data! And kudos to @emma-crepeau and @tim-khorev for their amazing solutions!!

Thanks!