Calculate a teams total goals from nested objects

Calculate a teams total goals from nested objects
0

#1

Hi,
Can anyone explain how I can write a function to calculate total goals scored for the season by a given team from the following data i’m fetching from an api.

I’m trying to use map, filter and reduce but need to get the teams home and away goals.

My effort so far is:

function run (teamName){

   function getSum (total, match) {
  return total + match.score1;
}
 
  
  fetch(url)
  .then(response => response.json())
  .then(data => console.log(data.rounds.map( matchday => matchday.matches.filter(match => match.team1.name === teamName).reduce(getSum))));
  
}

Sample of data returned (there are 10 matchdays):

{
  "name": "English Premier League 2014/15",
  "rounds": [
    {
      "name": "Matchday 1",
      "matches": [
        {
          "date": "2014-08-16",
          "team1": {
            "key": "manutd",
            "name": "Manchester United",
            "code": "MUN"
          },
          "team2": {
            "key": "swansea",
            "name": "Swansea",
            "code": "SWA"
          },
          "score1": 1,
          "score2": 2
        },
        {
          "date": "2014-08-16",
          "team1": {
            "key": "leicester",
            "name": "Leicester City",
            "code": "LEI"
          },
          "team2": {
            "key": "everton",
            "name": "Everton",
            "code": "EVE"
          },
          "score1": 2,
          "score2": 2
        },
        {
          "date": "2014-08-16",
          "team1": {
            "key": "qpr",
            "name": "Queens Park Rangers",
            "code": "QPR"
          },
          "team2": {
            "key": "hull",
            "name": "Hull City",
            "code": "HUL"
          },
          "score1": 0,
          "score2": 1
        },
        {
          "date": "2014-08-16",
          "team1": {
            "key": "stoke",
            "name": "Stoke City",
            "code": "STK"
          },
          "team2": {
            "key": "astonvilla",
            "name": "Aston Villa",
            "code": "AVL"
          },
          "score1": 0,
          "score2": 1
        },
        {
          "date": "2014-08-16",
          "team1": {
            "key": "westbrom",
            "name": "West Bromwich Albion",
            "code": "WBA"
          },
          "team2": {
            "key": "sunderland",
            "name": "Sunderland",
            "code": "SUN"
          },
          "score1": 2,
          "score2": 2
        },
        {
          "date": "2014-08-16",
          "team1": {
            "key": "westham",
            "name": "West Ham United",
            "code": "WHU"
          },
          "team2": {
            "key": "tottenham",
            "name": "Tottenham Hotspur",
            "code": "TOT"
          },
          "score1": 0,
          "score2": 1
        },
        {
          "date": "2014-08-16",
          "team1": {
            "key": "arsenal",
            "name": "Arsenal",
            "code": "ARS"
          },
          "team2": {
            "key": "crystalpalace",
            "name": "Crystal Palace",
            "code": "CRY"
          },
          "score1": 2,
          "score2": 1
        },
        {
          "date": "2014-08-17",
          "team1": {
            "key": "liverpool",
            "name": "Liverpool",
            "code": "LIV"
          },
          "team2": {
            "key": "southampton",
            "name": "Southampton",
            "code": "SOU"
          },
          "score1": 2,
          "score2": 1
        },
        {
          "date": "2014-08-17",
          "team1": {
            "key": "newcastle",
            "name": "Newcastle United",
            "code": "NEW"
          },
          "team2": {
            "key": "mancity",
            "name": "Manchester City",
            "code": "MCI"
          },
          "score1": 0,
          "score2": 2
        },
        {
          "date": "2014-08-18",
          "team1": {
            "key": "burnley",
            "name": "Burnley",
            "code": "BUR"
          },
          "team2": {
            "key": "chelsea",
            "name": "Chelsea",
            "code": "CHE"
          },
          "score1": 1,
          "score2": 3
        }
      ]
    },

#2

So there are a few ways to do this. Any way you do it means going over every entry, so as a suggestion, one way is to build an object that looks like this:

{
  "AFC Bournemouth": {
    home
    away
  },
  "Aston Villa": {
    home
    away
  },
  "Brighton & Hove Albion": {
    home
    away
  },
  ...
  "West Ham United": {
    home
    away
  }
}

You don’t need filter if that’s the case, just reduce will do (then you just query the object for the team you want).

So you would have the initial value for reduce (the accumulator) as {}. Then for each match in each round, you do

  1. Check if match.team1.name is in the accumulator: if it isn’t, add an object under it like [match.team1.name]: { home: 0, away: 0 }
  2. Check if match.team2.name is in the accumulator: if it isn’t, add an object under it like [match.team2.name]: { home: 0, away: 0 }

These steps are necessary in my algo, because if you just try to assign directly to home/away for a team that isn’t in the object you’ll get an undefined error. So then:

  1. Set accumulator[match.team1.name].home = accumulator[match.team1.name].home + match.score1
  2. Set accumulator[match.team2.name].away = accumulator[match.team2.name.away] + match.score2
  3. Return accumulator.

On your data, this gives you:

{
  "Manchester United": {
    "home": 1,
    "away": 0
  },
  "Swansea": {
    "home": 0,
    "away": 2
  },
  "Leicester City": {
    "home": 2,
    "away": 0
  },
  "Everton": {
    "home": 0,
    "away": 2
  },
  "Queens Park Rangers": {
    "home": 0,
    "away": 0
  },
  "Hull City": {
    "home": 0,
    "away": 1
  },
  "Stoke City": {
    "home": 0,
    "away": 0
  },
  "Aston Villa": {
    "home": 0,
    "away": 1
  },
  "West Bromwich Albion": {
    "home": 2,
    "away": 0
  },
  "Sunderland": {
    "home": 0,
    "away": 2
  },
  "West Ham United": {
    "home": 0,
    "away": 0
  },
  "Tottenham Hotspur": {
    "home": 0,
    "away": 1
  },
  "Arsenal": {
    "home": 2,
    "away": 0
  },
  "Crystal Palace": {
    "home": 0,
    "away": 1
  },
  "Liverpool": {
    "home": 2,
    "away": 0
  },
  "Southampton": {
    "home": 0,
    "away": 1
  },
  "Newcastle United": {
    "home": 0,
    "away": 0
  },
  "Manchester City": {
    "home": 0,
    "away": 2
  },
  "Burnley": {
    "home": 1,
    "away": 0
  },
  "Chelsea": {
    "home": 0,
    "away": 3
  }
}

It may be useful to do this as you then have a data structure that you can use to get any team’s home/away goals.


To get a single team’s goals, you can do the same thing, but instead of building it for every one, you can start the accumulator as just:

{home: 0, away: 0}

Then in the callback, you don’t need to do the initial check/object building. You just check if match.team1.name is equal to the name you want: if so add score1 to home. And if match.team2.name is equal to the one you want, add score2 to away.

For loops are better for the latter, because you want to stop it looking in the matches for that matchday as soon as you’ve found the goals scored: any given team can only play once per matchday, so there’s no point checking the rest.


#3

Thanks very much. I’ve now got this code but getting error Uncaught (in promise) ReferenceError: rounds is not defined.

Is this because its trying to run for loops before data received? I thought async await would solve this?

const getScore = async (teamName) => {
    const response = await fetch(url);
    const json = await response.json();
    console.log(json.rounds)
//if(json){console.log('hi' + json)}

let score = 0;
  
for(round of json.rounds){
  for(match of rounds.matches){
    if(match.team1.name === teamName){ 
      score += match.score1
    }else if(match.team1.name === teamName){
      score += match.score2
    } 
  }
 }

  console.log(score)
}

getScore('Everton')

#4

It is round.matches not rounds.matches

for(match of round.matches)

Edit: Assuming this is the api


#5

Hell, I was going back and forth through the code thinking “this all looks spot on, can’t see why it would be blowing up, and the data is correct…” :roll_eyes:


#6

Thanks for your help guys, round.matches works! Above I have match.team1 in the else if which of course should be match.team2. Once i fixed this it worked.

That is the correct api, thanks lasjorg.

I also added in a check in case the team entered is not present or misspelled, prior to making a network request. I assume this is more efficient than checking after making a request.

Code:

const getTeamScore = async (teamName) => {

const teamNames = [‘Arsenal’, ‘Aston Villa’, ‘Burnley’, ‘Chelsea’, ‘Crystal Palace’,
‘Everton’, ‘Hull City’, ‘Leicester’, ‘Liverpool’, ‘Manchester City’, ‘Manchester United’,‘Newcastle United’, ‘Queens Park Rangers’, ‘Southampton’, ‘Stoke City’, ‘Sunderland’, ‘Swansea City’, ‘Totenham Hotspur’, ‘West Bromwich Albion’, ];

if(!teamNames.find(team => team === teamName)){
return console.log(‘Team does not exist in the league please check your spelling’)
}

const response = await fetch('https://raw.githubusercontent.com/openfootball/football.json/master/2014-15/en.1.json');
const json = await response.json();

let score = 0;

for(round of json.rounds){
for(match of round.matches){
if(match.team1.name === teamName){
score += match.score1
}else if(match.team2.name === teamName){
score += match.score2
}else{
score = score
}

}
//console.log(score)
}

console.log('Total goals scored for the season by ’ + teamName + ’ were: ’ + score )

}

getTeamScore(‘Liverpool’);