Home > Software engineering >  How can I sum the sum of a property of an javascript array in an array of objects?
How can I sum the sum of a property of an javascript array in an array of objects?

Time:07-04

I'm making a game where each player can submit an answer on a given question and other players can vote on an answer of another player. I store these results in an array of game rounds which may look something like the following:

const roundHistory = [
  {
    question: 'question1',
    submissions: [
      {
        explanation: 'answer1',
        player: { id: 'id1', name: 'player1' },
        votes: [
          { id: 'id2', name: 'player2' },
          { id: 'id3', name: 'player3' },
        ],
      },
      {
        explanation: 'answer2',
        player: { id: 'id2', name: 'player2' },
        votes: [{ id: 'id1', name: 'player1' }],
      },
      {
        explanation: 'answer3',
        player: { id: 'id3', name: 'player3' },
        votes: [],
      },
    ],
  },
  {
    question: 'question2',
    submissions: [
      {
        explanation: 'answer1',
        player: { id: 'id1', name: 'player1' },
        votes: [
          { id: 'id2', name: 'player2' },
          { id: 'id3', name: 'player3' },
        ],
      },
      {
        explanation: 'answer2',
        player: { id: 'id2', name: 'player2' },
        votes: [{ id: 'id1', name: 'player1' }],
      },
      {
        explanation: 'answer3',
        player: { id: 'id3', name: 'player3' },
        votes: [],
      },
    ],
  },
];

As you can see there are 3 players in the game since each round (index of the roundHistory array) has 3 submissions. Each submission has a property player which represents the player who submitted the submission. And then each submission has a property votes which is an array of the players who voted for that submission. Now my question:

How can I get the total received votes per player of all rounds?


What I've tried so far... I thought, first I'd get the total received votes per player per round like so:

const roundHistory = [{question:'question1',submissions:[{explanation:'answer1',player:{id:'id1',name:'player1'},votes:[{id:'id2',name:'player2'},{id:'id3',name:'player3'},],},{explanation:'answer2',player:{id:'id2',name:'player2'},votes:[{id:'id1',name:'player1'}],},{explanation:'answer3',player:{id:'id3',name:'player3'},votes:[],},],},{question:'question2',submissions:[{explanation:'answer1',player:{id:'id1',name:'player1'},votes:[{id:'id2',name:'player2'},{id:'id3',name:'player3'},],},{explanation:'answer2',player:{id:'id2',name:'player2'},votes:[{id:'id1',name:'player1'}],},{explanation:'answer3',player:{id:'id3',name:'player3'},votes:[],},],},];

const getTotalReceivedVotesPerPlayerInRound = (round) => {
  return roundHistory[round].submissions.map((s) => ({
    player: s.player,
    totalVotes: s.votes.length,
  }));
}

console.log(getTotalReceivedVotesPerPlayerInRound(0));

But I got stuck at adding all results of all rounds together. Note that I tried to simplify all used objects such as a player. In my application these are mostly classes for which I have several utility methods. One of them is the getTotalReceivedVotesPerPlayerInRound() method that I can use on a round (index of the roundHistory array).

I expect the final result to be of the same shape as the result of my already written function: An array of objects with two properties: player (the player object) and totalVotes. E. g.:

const result = [
  {
    "player": {
      "id": "id1",
      "name": "player1"
    },
    "totalVotes": 2
  },
  {
    "player": {
      "id": "id2",
      "name": "player2"
    },
    "totalVotes": 1
  },
  {
    "player": {
      "id": "id3",
      "name": "player3"
    },
    "totalVotes": 0
  }
];

CodePudding user response:

Presented below is one possible way to achieve the desired objective.

Code Snippet

const getPlayerTotalVotes = arr => (
  Object.values(
    arr.reduce(
      (acc, { submissions}) => {
        submissions.forEach(
          ({ player: { id, name }, votes }) => {
            acc[id] ??= { player: { id, name }, totalVotes: 0 };
            acc[id].totalVotes  = votes.length;
          }
        )
        return acc;
      },
      {}
    )
  )
);
/* explanation of the code
method to obtain the total votes per-player for all rounds
const getPlayerTotalVotes = arr => (
  Object.values(      // extract only the values of the below intermediate result object
  arr.reduce(         // ".reduce()" to iterate and sum votes-count
    (acc, { submissions}) => {    // "acc" is the accumulator; destructure iterator
      // to directly access "submissions" array
      // then, iterate using ".forEach()" over "submissions"
      submissions.forEach(
        // destructure to access "votes" array and the particular player "id"
        ({ player: { id }, votes }) => {
          // conditionally-assign 0 if "id" not already present in "acc" 
          acc[id] ??= 0;
          // count the "votes" and add it to the "acc" prop with key as "id"
          acc[id]  = votes.length;
        }
      )
      return acc;   // explicitly return "acc" for each iteration
    },
    {}        // "acc" is initialized as an empty object
  )       // implicit return of the result from ".reduce()" to caller
  )
);
*/
const roundHistory = [{
    question: 'question1',
    submissions: [{
        explanation: 'answer1',
        player: {
          id: 'id1',
          name: 'player1'
        },
        votes: [{
            id: 'id2',
            name: 'player2'
          },
          {
            id: 'id3',
            name: 'player3'
          },
        ],
      },
      {
        explanation: 'answer2',
        player: {
          id: 'id2',
          name: 'player2'
        },
        votes: [{
          id: 'id1',
          name: 'player1'
        }],
      },
      {
        explanation: 'answer3',
        player: {
          id: 'id3',
          name: 'player3'
        },
        votes: [],
      },
    ],
  },
  {
    question: 'question2',
    submissions: [{
        explanation: 'answer1',
        player: {
          id: 'id1',
          name: 'player1'
        },
        votes: [{
            id: 'id2',
            name: 'player2'
          },
          {
            id: 'id3',
            name: 'player3'
          },
        ],
      },
      {
        explanation: 'answer2',
        player: {
          id: 'id2',
          name: 'player2'
        },
        votes: [{
          id: 'id1',
          name: 'player1'
        }],
      },
      {
        explanation: 'answer3',
        player: {
          id: 'id3',
          name: 'player3'
        },
        votes: [],
      },
    ],
  },
];

console.log(
  'total votes per player\n',
  getPlayerTotalVotes(roundHistory)
);
.as-console-wrapper { max-height: 100% !important; top: 0 }

Explanation

Inline comments added to the snippet above.

EDIT Using the given method to obtain sum of votes "per-round"

const roundHistory = [{question:'question1',submissions:[{explanation:'answer1',player:{id:'id1',name:'player1'},votes:[{id:'id2',name:'player2'},{id:'id3',name:'player3'},],},{explanation:'answer2',player:{id:'id2',name:'player2'},votes:[{id:'id1',name:'player1'}],},{explanation:'answer3',player:{id:'id3',name:'player3'},votes:[],},],},{question:'question2',submissions:[{explanation:'answer1',player:{id:'id1',name:'player1'},votes:[{id:'id2',name:'player2'},{id:'id3',name:'player3'},],},{explanation:'answer2',player:{id:'id2',name:'player2'},votes:[{id:'id1',name:'player1'}],},{explanation:'answer3',player:{id:'id3',name:'player3'},votes:[],},],},];

const getTotalReceivedVotesPerPlayerInRound = (round) => {
  return roundHistory[round].submissions.map((s) => ({
    player: s.player,
    totalVotes: s.votes.length,
  }));
}

const getTotalAllRounds = rh => (
  Object.values(
    rh.reduce(
      (acc, _, idx) => {
        getTotalReceivedVotesPerPlayerInRound(idx)?.forEach(
          ({player: { id, name}, totalVotes}) => {
            acc[id] ??= { player: {id, name}, totalVotes: 0 };
            acc[id].totalVotes  = totalVotes;
          }
        );
        return acc;
      },
      {}
    )
  )
);

console.log(getTotalAllRounds(roundHistory));
.as-console-wrapper { max-height: 100% !important; top: 0 }

  • Related