JavaScript function to create 2 soccer teams as even as possible, from a list of players

Posted on

Problem

? Given a list of players with their respective scores (how well does a player play soccer, from 1 to 5 points), return two teams that are as even as possible.

I’ve created this code, which is a first version, but I really would like to hear about ideas of how can I improve it.

``````exports.TeamCreator = (function () {

this.players = [];

function setPlayers(players){
this.players = Object.assign([], players);
}

function createTeams() {

var input = this.players;

var teams = [],
i = 0,
scores = [],
max = input.length;

scores["A"]=0; scores["B"]=0;
teams["A"]=[]; teams["B"]=[];

input.sort(function(a, b){ return b.score - a.score;});

var nextPlayer;
var teamForNextPlayer;

for (i = 0; i < max; i++){

next();
teams[teamForNextPlayer].push(nextPlayer);
scores[teamForNextPlayer] += nextPlayer.score;
}

function getBest() {
nextPlayer = input.splice(0, 1)[0]; //Best in list
}
function getWorst() {
nextPlayer =  input.splice(input.length - 1, 1)[0]; //Worst in list
}
function next(){
var counterA = teams["A"],
counterB = teams["B"],
scoreA = scores["A"],
scoreB = scores["B"];

if(counterA > counterB) { //B has less players than A
teamForNextPlayer = "B";

if(scoreB >= scoreA){
getWorst();
}else{
getBest();
}
}
else if(counterA < counterB) { //A has less players than B
teamForNextPlayer = "A";
if(scoreA >= scoreB){
getWorst();
}else{
getBest();
}
}
else //Same amount of players
{
getBest();
teamForNextPlayer = (scoreA >= scoreB) ? "B" : "A";
}
}

return {
teamA: teams["A"],
teamB: teams["B"],
diff: Math.abs(scores["A"] - scores["B"]),
scoreA: scores["A"],
scoreB: scores["B"]
};

}
return {
setPlayers : setPlayers,
createTeams: createTeams
}
}());
``````

If it’s easier, you can pull the code from here.

Solution

First I would make each team an object with a score and players rather than an object for scores and one for players:

``````var teams = [ { name:'A'; score: 0, players:[] },
{ name:'B', score: 0, players:[] };
``````

Once you have sorted the players by score it might be simpler to pick them off the list two at a time and assign the one with the higher score to the lower scoring team:

``````// Note: slice is probably better than Object.assign
let input = this.players.slice();
assert(input.length % 2 == 0, 'Need an equal number of players');

while (input.length > 0) {
let [strongerTeam,   weakerTeam]   = teams;
let [strongerPlayer, weakerPlayer] = input.slice(0,2);
sortTeams(teams);
}

team.players.push(player);
team.score += player.score;
}

// Sort highest scoring team first
function sortTeams(teams) {
teams.sort(function(a, b) { return b.score - a.score;});
}

return teams;
``````

One advantage is this always returns the more powerful team first.

This algorithm is not ideal in that it doesn’t always choose the most balanced team, for example given players with scores of 8, 4, 3, 3, 1 and 1, Team A will have players 8, 3 and 1 (score of 12) while team b will have players with scores of 4, 3 and 1 (totaling 8). It might be more reasonable to balance them as Team A: 8, 1, 1 and Team B 4, 3, 3 so that both teams have a score of 10. The logic for that might be:

``````while (input.length > 0) {
let [strongerTeam,   weakerTeam]   = teams;
let nextPlayer = input.slice(0,1)[0];
if (strongerTeam.length - weakerTeam.length == input.length) {