gvon79
gvon79

Reputation: 662

Using reduce to group by and sum

I want to return an array grouped by the team with gp, win, loss summed up. I'm trying to accomplish this with reduce, however, the totals are not adding up. Here's my code...

const myArr = [
  {team: 'Red', gp: 3, win:2, loss:1},
  {team: 'Black', gp: 3, win:1, loss:2},
  {team: 'Red', gp: 10, win:8, loss:2}
]

let output = myArr.reduce(
  (acc, curr) => {
    acc[curr.team] = {
      gp: acc.gp + curr.gp,
      win: acc.win + curr.win,
      loss: acc.loss + curr.loss
    };
    return acc;
  }, {
    gp: 0,
    win: 0,
    loss: 0
  }
);

console.log(output);

This code returns the array in the format I need, however, the gp, win, loss is not summed up, instead it shows the last data point.

Upvotes: 1

Views: 1387

Answers (3)

PinguinoSod
PinguinoSod

Reputation: 85

You could pass an empty array to the accumulator, then using the team property as key, you can check if those values are already defined on the accumulator array, if so then you sum the already defined values with the current ones, otherwise you just take the current values.

const myArr = [
  {team: 'Red', gp: 3, win:2, loss:1},
  {team: 'Black', gp: 3, win:1, loss:2},
  {team: 'Red', gp: 10, win:8, loss:2}
]

let output = myArr.reduce(
  (acc, curr) => {
    acc[curr.team] = {
      gp: acc[curr.team]?.gp ? acc[curr.team].gp + curr.gp : curr.gp,
      win: acc[curr.team]?.win ? acc[curr.team].win + curr.win : curr.win,
      loss: acc[curr.team]?.loss ? acc[curr.team].loss + curr.loss : curr.loss
    };
    return acc;
  }, []
);

console.log(output);

Upvotes: 0

Get Off My Lawn
Get Off My Lawn

Reputation: 36311

I would reduce the items to a unique set using Set, then map each item to a new object.

const teams = [{team: 'Red', gp: 3, win:2, loss:1},
{team: 'Black', gp: 3, win:1, loss:2},
{team: 'Red', gp: 10, win:8, loss:2}]

let result = [...new Set(teams.map(t => t.team))].map(team => {
  return {
    team,
    gp:   teams.reduce((sum, val) => val.team == team ? sum + val.gp   : sum, 0),
    win:  teams.reduce((sum, val) => val.team == team ? sum + val.win  : sum, 0),
    loss: teams.reduce((sum, val) => val.team == team ? sum + val.loss : sum, 0)
  }
}, [])

console.log(result)

Upvotes: 0

Nina Scholz
Nina Scholz

Reputation: 386624

You need to take an empty object as accumulator and then you could take the wanted keys for adding.

const
    myArr = [{ team: 'Red', gp: 3, win: 2, loss: 1 }, { team: 'Black', gp: 3, win: 1, loss: 2 }, { team: 'Red', gp: 10, win: 8, loss: 2 }],
    keys = ['gp', 'win', 'loss'],
    output = myArr.reduce((acc, curr) => {
        acc[curr.team] = acc[curr.team] || Object.assign(...keys.map(k => ({ [k]: 0})));
        keys.forEach(k => acc[curr.team][k] += curr[k]);
        return acc;
  }, Object.create(null));

console.log(output);

Upvotes: 3

Related Questions