0
{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5", Status: "Done", Month: "May" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10", Status: "Started", Month: "May" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15", Status: "Done", Month: "June" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20", Status: "Started", Month: "Aug" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25", Status: "Done", Month: "Apr" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30", Status: "NotStarted", Month: "May" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35", Status: "Done", Month: "Oct" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40", Status: "NotStarted", Month: "Dec" }

I need to get the total of value from the above JSon, group by based on 2 fields. The fields can be dynamic. For eg the group can Phase and Step the the output should be like

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

or the group by can be Phase & Status

I used the below code to group by Phase and Step. But I want the group by parameters to be dynamic and decided at runtime

result = Object.values(
  data.reduce((r, o) => {
    const key = `${o.Phase}_${o.Step}`;
    r[key] = r[key] || { Phase: o.Phase, Step: o.Step, Task: [], Value: 0 };
    r[key].Task.push(o.Task);
    r[key].Value += +o.Value;
    return r;
  }, {})
).map((o) => ({ ...o, Task: o.Task.join(",") }));
Ben Wainwright
  • 4,224
  • 1
  • 18
  • 36

1 Answers1

0

My code below works with any number of keys. Define your wanted keys.

Not sure whether you wanted to concatenate all other fields though.

const data = [{ Phase: "Phase 1", Step: "Step 1", Task: "Task 1", Value: "5", Status: "Done", Month: "May" },
    { Phase: "Phase 1", Step: "Step 1", Task: "Task 2", Value: "10", Status: "Started", Month: "May" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 1", Value: "15", Status: "Done", Month: "June" },
    { Phase: "Phase 1", Step: "Step 2", Task: "Task 2", Value: "20", Status: "Started", Month: "Aug" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 1", Value: "25", Status: "Done", Month: "Apr" },
    { Phase: "Phase 2", Step: "Step 1", Task: "Task 2", Value: "30", Status: "NotStarted", Month: "May" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 1", Value: "35", Status: "Done", Month: "Oct" },
    { Phase: "Phase 2", Step: "Step 2", Task: "Task 2", Value: "40", Status: "NotStarted", Month: "Dec" }];

const keys = ["Phase", "Step"];
function matches(table, entry, keys) { // finds item with same values
  return table.find(e => keys.every(k => e[k] == entry[k]));
}
const result = data.reduce((cur, val) => {
  let alreadyIn = matches(cur, val, keys);
  if (alreadyIn) {
    alreadyIn['Value'] = (parseInt(alreadyIn['Value']) + parseInt(val['Value'])).toString();
  } else {
    cur.push(val);
  }
  return cur;
}, []);

console.log(result);
h0ly
  • 161
  • 1
  • 8