Dênis Fernandes
Dênis Fernandes

Reputation: 174

C# LINQ - Order List by an int and alternate result between other int "odd and even"

I'm trying to order a list of players for a turn based game and i have the following data structure:

public class PlayerTurn {
  public int energy;
  public int team; //Can be 0 or 1
}

public List<PlayerTurn> turnList = new List<PlayerTurn>();

Let's say that we have 4 players, 2 on each team like this:

Player Energy Team
Player 1 10 0
Player 2 30 0
Player 3 5 1
Player 4 60 1

How i can order this list in a way that the player with most energy will be first, but alternating between the teams while keeping the order by energy? The result expected is something like:

Player Energy Team
Player 4 60 1
Player 2 30 0
Player 3 5 1
Player 1 10 0

Is possible to achieve that only with Linq? Without using Linq is also acceptable.

EDIT: Here's the link with a test of two approaches by @GianPaolo and @VadimMartynov https://dotnetfiddle.net/0O22dX

Upvotes: 0

Views: 163

Answers (2)

Vadim Martynov
Vadim Martynov

Reputation: 8902

  1. Order players by their energy with OrderByDescending.
  2. Group they by team with GroupBy. After that you will have 2 ordered groups for each team.
  3. Tricky moment! Now you can merge this 2 lists in 1 structure with extra index property from each list. We use teamIndex as an index of team and playerIndex as a players index inside their team. We create a new objects for each player with 3 properties: player himself, team (group) index, player index. Here we use anonymous types to create an object without type declaration.
  4. Now order result by player index with OrderBy.
  5. And then by team index with ThenBy.
  6. Select a player with Select.
var result = turnList
    .OrderByDescending(p => p.energy)
    .GroupBy(p => p.team)
    .SelectMany((team, teamIndex) => team
        .Select((player, playerIndex) => new { player, teamIndex, playerIndex }))
    .OrderBy(item => item.playerIndex)
    .ThenBy(item => item.teamIndex)
    .Select(item => item.player)
    .ToList();

It's important to use teamIndex in the step 3 because team with the best player always be in [0]-th place in a grouped list because we ordered all players in the step 1 first.

Upvotes: 2

Gian Paolo
Gian Paolo

Reputation: 4249

I'd go splitting the list in two, one for each team. then sort both of them and then, very old style, pick one element at time from each list

List<PlayerTurn> allPlayers = new List<PlayerTurn>();// populated somehow

List<PlayerTurn> teamOne = allPlayer.Where (p=> p.Team = 1).OrderByDescending(p=>p.energy).ToList(); 
List<PlayerTurn> teamZero = allPlayer.Where (p=> p.Team = 0).OrderByDescending(p=>p.energy).ToList(); 

// which team go first?
var firstTeam = teamOne[0].energy >= teamZero[0].energy ? teamOne : teamZero;
var secondTeam = firstTeam == teamZero ? teamOne : teamZero;

var sorted = new List<PlayerTurn>();

// now zip the two teams
for (int i = 0; i < teamOne.Count(); i++) // TODO: check lists are same length
{
    sorted.Add(firstTeam [i]);
    sorted.Add(secondTeam [i]);
}

return sorted;

Upvotes: 2

Related Questions