Back to Schedule

Week 7 Lab: Laser Tag Leaderboard Part 3

This lab is due on 3/21 at 11:59pm

For Lab 7, please complete the TeamLeaderboard component of your Leaderboard application. This should follow similar steps that you completed above for IndividualLeaderboard, except each row should reflect a team and the scores be the sum of the scores for all players in the team. The team leaderboard should also be ranked with the leading team as the first row.

Hint: It will be easiest to store teams as an object (as a sort of dictionary keyed by team name) in the state of Main.js, along with players, so you have something like this in the constructor:

constructor(props) {
    super(props);
    this.state = {
        players: [],
        teams: {}
    };

    this.addToPlayers = this.addToPlayers.bind(this);
}

this.state.teams would have this format so that it is easy to look up team by team name:

{
    Red: {
        name: 'Red',
        score: 100
    },
    Blue: {
        name: 'Blue',
        score: 200
    }
}

Why an object instead of an array? With an object that is keyed on the team name, it will be easier to update teams accordingly whenever additional players are added in addToPlayers. With an array, your update method would have to find the right team in the array to update, splice it out, and replace it, every time an update has to be made to keep the immutability of the React state intact.

If this.state.teams was an array, your update method might have to look something like this:

updateTeams(newPlayer) {
    const newPlayerTeamName = newPlayer.team;
    const teams = this.state.teams;
    let team;
    let teamIndex;
    for (let i = 0; i < teams.length; i++) {
        if (teams[i].name === newPlayerTeamName) {
            team = teams[i];
            teamIndex = i;
            break;
        }
    }

    let updatedTeam;
    let updatedTeams;
    if (!team) {
        updatedTeam = {
            name: newPlayerTeamName,
            score: newPlayer.score
        };
        updatedTeams = teams.concat(updatedTeam);
    } else {
        updatedTeam = Object.assign({}, team);
        updatedTeam.score += newPlayer.score;
        updatedTeams.teams.splice(teamIndex, 1, updatedTeam);
    }

    this.setState({ teams: updatedTeams });
}

Pretty complicated, huh… With an Object, it is much easier, because we can key on team name as such:

updateTeams(newPlayer) {
    const teams = Object.assign({}, this.state.teams);
    const newPlayerTeamName = newPlayer.team;
    const team = Object.assign({}, teams[newPlayerTeamName]);
    
    if (!team.name) {
        team.name = newPlayerTeamName;
    }

    if (!team.score) {
        team.score = 0;
    }
    team.score += newPlayer.score;

    teams[newPlayerTeamName] = team;
    this.setState({ teams: teams });
}

We can get a list of the keys in an object with Object.keys, and then map them to an array of the object values to use in our templates in render():

{Object.keys(this.props.teams)
    .map((teamName) => this.props.teams[teamName])
    .sort((teamA, teamB) => {
        return teamA.score < teamB.score;
    })
    .map((team, index) => {
        return (<Component key={index} rank={index + 1} team={team} />);
    });

Solutions: Lab 7 Solutions