0

Phrasing for the specific data manipulation I am working on is difficult, so pardon for the poor title - I will bounce back with a good example. I have the following javascript array of objects, containing some sports data:

[
    { team: "Knicks", assists: 24 }, 
    { team: "Knicks", assists: 12 }, 
    { team: "Knicks", assists: 17 }, 
    { team: "Knicks", assists: 19 }, 
    { team: "Warriors", assists: 31 }, 
    { team: "Warriors", assists: 25 }, 
    { team: "Warriors", assists: 20 }, 
    { team: "Spurs", assists: 15 }, 
    { team: "Spurs", assists: 17 }, 
    { team: "Spurs", assists: 32 }, 
    { team: "Spurs", assists: 12 }, 
    { team: "Spurs", assists: 18 }
]

and would like to tidy it up so that the data looks like this:

[
    { team: "Knicks", assists: [24, 36, 53, 72] },
    { team: "Warriors", assists: [31, 56, 76] },
    { team: "Spurs", assists: [15, 32, 64, 76, 94] }

]

each unique team in the original array of objects receives its own object in the new array, and the assist values are now the cumulative sum of the assist values. I am fairly certain that it will always be the case that the original array of objects is ordered in the correct manner, such that looping top to bottom will produce the correct cumulative sum.

Any help with this is greatly appreciated, thanks!

2
  • 3
    any attempts on this? can you share? Commented Jul 19, 2018 at 20:14
  • does "tidy up" not mean "write my code"? Show what you tried Commented Jul 19, 2018 at 20:16

4 Answers 4

3

You could use reduce method to build an object and then Object.values method to get array of objects.

const data = [{"team":"Knicks","assists":24},{"team":"Knicks","assists":12},{"team":"Knicks","assists":17},{"team":"Knicks","assists":19},{"team":"Warriors","assists":31},{"team":"Warriors","assists":25},{"team":"Warriors","assists":20},{"team":"Spurs","assists":15},{"team":"Spurs","assists":17},{"team":"Spurs","assists":32},{"team":"Spurs","assists":12},{"team":"Spurs","assists":18}]

const result = data.reduce((r, {team, assists}) => {
  if(!r[team]) r[team] = {team, assists: [assists]}
  else r[team].assists.push(r[team].assists.slice(-1)[0] + assists);
  return r;
}, {})

console.log(Object.values(result))

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

4 Comments

Appreciate the quick response on this - i figured reduce was the correct approach and had attempted with it, to no luck. I should hopefully be able to complete this on my end adjusting this to instead return the cumulative sum in the array
as shown in the original Q
changing the else() case to: r[team].assists.push(r[team].assists[r[team].assists.length - 1] + assists); does the trick, albeit a bit messy
Didn't notice that, updated my answer.
2

Here is functional programming approach, using a temporary ES6 Map:

const teams = [{ team: "Knicks", assists: 24 },{ team: "Knicks", assists: 12 },{ team: "Knicks", assists: 17 },{ team: "Knicks", assists: 19 },{ team: "Warriors", assists: 31 },{ team: "Warriors", assists: 25 },{ team: "Warriors", assists: 20 },{ team: "Spurs", assists: 15 },{ team: "Spurs", assists: 17 },{ team: "Spurs", assists: 32 },{ team: "Spurs", assists: 12 },{ team: "Spurs", assists: 18 }];

const result = Array.from(
    teams.reduce( (acc, {team, assists}) => acc.set(team, (acc.get(team) || []).concat(assists)), new Map),
    ([team, assists]) => ({team, assists})
);

console.log(result);

Comments

1

You can also make use of array#forEach() method like this:

const teams= [
    { team: "Knicks", assists: 24 }, 
    { team: "Knicks", assists: 12 }, 
    { team: "Knicks", assists: 17 }, 
    { team: "Knicks", assists: 19 }, 
    { team: "Warriors", assists: 31 }, 
    { team: "Warriors", assists: 25 }, 
    { team: "Warriors", assists: 20 }, 
    { team: "Spurs", assists: 15 }, 
    { team: "Spurs", assists: 17 }, 
    { team: "Spurs", assists: 32 }, 
    { team: "Spurs", assists: 12 }, 
    { team: "Spurs", assists: 18 }
]

let teamGroup = {}

teams.forEach(team => {
  teamGroup[team.team] ? // check if that array exists or not in teamgroup object
    teamGroup[team.team].push(team.assists)  // just push
   : (teamGroup[team.team] = [], teamGroup[team.team].push(team.assists)) // create a new array and push
})

console.log(teamGroup);

2 Comments

This doesn't give the right result. It gives {Knicks: [{assists: 31}, {assists: 25}, {assists: 20}]}, instead of [{team: "Knicks", assists: [31, 25, 20]}].
You can just push like " teamGroup[team.team].push(team.assists)) ". I have updated the answer above. I hope now it solves your issue.
1

The lodash library has a handy _.groupBy that does a fair bit of the work for you.

If you do const groups = _.groupBy(data, team), you'll get:

{
    "Knicks": [
        { "team": "Knicks", "assists": 24},
        { "team": "Knicks", "assists": 12},
        { "team": "Knicks", "assists": 17},
        { "team": "Knicks", "assists": 19},
    ],
    // ...
}

Then,

const result = _.map(groups, (teamData, teamName) => {
    // Convert teamData array into a single record
    const assists = _.map(teamData, "assists"); // e.g. [24, 12, 17, 19]
    return { team: teamName, assists: runningTotal(assists) };
});

// Turns an array like [1,2,3,4] into an array like [1,3,6,10]
function runningTotal(array) {
    let prev = 0;
    return array.map(next => {
        prev = next + prev;
        return prev;
    });
}

This gives the desired outcome.


This approach isn't significantly shorter than, say, a solution that uses reduce, but personally I find higher-order operations like groupBy and map easier to reason about than reduce. (However, of course, this requires either lodash, or a similarly implemented groupBy function)


EDIT: I completely missed the bit where the assists wasn't just an array of assists, but a running total. Added a bit to account for that. The code is much longer than the reduce solution, but IMO, way clearer.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.