Home > Net >  Get object with the greatest value for a key from an array JavaScript
Get object with the greatest value for a key from an array JavaScript

Time:05-04

I have an matchesScoreResult array of objects like this:

[
  {
    roundId: '397a57f6-c9da-4017-bf98-62d7d48c1da5',
    teamId: '32305c41-00e8-492a-859c-83c262230e06',
    score: '7'
  },
  {
    roundId: '397a57f6-c9da-4017-bf98-62d7d48c1da5',
    teamId: '1122ef35-8bce-4310-838b-8221228cadc9',
    score: '18'
  },
  {
    roundId: 'c91f1a16-df97-4716-bb0d-8589612da704',
    teamId: '32305c41-00e8-492a-859c-83c262230e06',
    score: '21'
  },
  {
    roundId: 'c91f1a16-df97-4716-bb0d-8589612da704',
    teamId: '1122ef35-8bce-4310-838b-8221228cadc9',
    score: '19'
  }
]

This is an array for some rounds in a game (as you can see, the roundId key is found twice the same, because there are two teams that play in the same round, and the case is that the same two teams played two different rounds)

Based on the roundId and the team that won I want to increment firstTeamRoundsWon variable or secondTeamRoundsWon.

First I get a unique round ids array like this:

let uniqueRoundIds = [...new Set(matchesScoreResult.map(item => item.roundId))]

Based on that uniqueRoundIds array I do the following operations:

uniqueRoundIds.map(roundId => matchesScoreResult.filter(teamRound => teamRound.roundId === roundId)
.map(round => round.reduce((previousValue, currentValue) => previousValue.score > currentValue.score ? firstTeamRoundsWon   : secondTeamRoundsWon  ))

My problem is that it increments twice the firstTeamRoundsWon but based on my data, both variables should be 1.

Is there something wrong that I did there?

I am open to other ways of resolving this.

Thank you for your help!

CodePudding user response:

You can check the below logic with some comments

const matchesScoreResult = [{
    roundId: '397a57f6-c9da-4017-bf98-62d7d48c1da5',
    teamId: '32305c41-00e8-492a-859c-83c262230e06',
    score: '7'
  },
  {
    roundId: '397a57f6-c9da-4017-bf98-62d7d48c1da5',
    teamId: '1122ef35-8bce-4310-838b-8221228cadc9',
    score: '18'
  },
  {
    roundId: 'c91f1a16-df97-4716-bb0d-8589612da704',
    teamId: '32305c41-00e8-492a-859c-83c262230e06',
    score: '21'
  },
  {
    roundId: 'c91f1a16-df97-4716-bb0d-8589612da704',
    teamId: '1122ef35-8bce-4310-838b-8221228cadc9',
    score: '19'
  }
]

//grouBy is not available in browsers yet, so need to have polyfill for it
const groupBy = function(data, key) {
  return data.reduce(function(current, value) {
    (current[value[key]] = current[value[key]] || []).push(value);
    return current;
  }, {});
};

//group similar rounds to become matches
const matches = groupBy(matchesScoreResult, 'roundId')

//find all unquie team ids
let teams = [...new Set(matchesScoreResult.map(item => item.team))]

const teamScores = teams.reduce((teamData, team) => {
  //loop through all matches with the first and second round data
  for (const [firstRound, secondRound] of Object.values(matches)) {
    //count for the team win the first round
    if (Number(firstRound.score) > Number(secondRound.score)) {
      if (!teamData[firstRound.teamId]) {
        teamData[firstRound.teamId] = 0
      }
      teamData[firstRound.teamId]  = 1
    }
    
    //count for the team win the second round
    if (Number(firstRound.score) < Number(secondRound.score)) {
      if (!teamData[secondRound.teamId]) {
        teamData[secondRound.teamId] = 0
      }
      teamData[secondRound.teamId]  = 1
    }
  }
  return teamData
}, {})


//print out all team ids with the counts
console.log(teamScores)

//convert `teamScores` to your desired values
const [firstTeamRoundsWon, secondTeamRoundsWon] = Object.values(teamScores)
console.log({ firstTeamRoundsWon, secondTeamRoundsWon })

CodePudding user response:

I believe your problem is with a misunderstanding of the reduce function. The previous value is whatever returns from the last iteration it did. In this case it will be either the value of firstTeamRoundsWon or secondTeamRoundsWon. And a number.score returns undefined. The reduce function is meant to be used to take an array and reduce it down to a single value in some way.

I don't think using firstTeamRoundsWon and secondTeamRoundsWon is the best way to keep track of how many times each time has won as that seems very dependant on the order of the array.

Instead I'd suggest an object with the keys being the teamID and the value being the number of times they've won.

const teamWins = {}
[...new Set(matchesScoreResult.map(round => round.teamId))].forEach(teamId => teamWins[teamId] = 0)
[...new Set(matchesScoreResult.map(round => round.roundId))].forEach(roundId => {
    // Assuming there will only ever be 2 teams per round
    const teams = matchesScoreResult.filter(round => round.roundId === roundId)
    teamWins[teams[teams[0].score > teams[1].score ? 0 : 1].teamID]  
    // If teams had a draw here then the second listed team in the array would have a win added.
})
console.log(teamWins)

btw. You should use map if you want to mutate the array into another form and forEach if you just want to iterate over each result and don't care about it returning a value.

  • Related