Question

Je construis un plug-in pour un site de LAN party que j'ai écrit qui permettrait l'utilisation d'un tournoi Round Robin.

Tout va bien, mais j'ai quelques questions sur la façon la plus efficace de classer sur deux critères.

En fait, je voudrais la mise en page de classement suivant:

         Rank  Wins  TotalScore
PersonE  1     5     50
PersonD  2     3.5   37
PersonA  2     3.5   37
PersonC  4     2.5   26
PersonB  5     2.5   24
PersonF  6     0     12

Dans le serveur SQL, j'utiliser:

SELECT
    [Person],
    RANK() OVER (ORDER BY Wins DESC, TotalScore DESC) [Rank],
    [Wins],
    [TotalScore]

Maintenant, je ne List, Dictionnaire, etc. et de travailler avec

Plus précisément:

Dictionary<TournamentTeam, double> wins = new Dictionary<TournamentTeam, double>();
Dictionary<TournamentTeam, double> score = new Dictionary<TournamentTeam, double>();

Est-il possible de faire ce style de classement avec LINQ?

Dans le cas contraire, est-il un extensible chemin qui me permettrait plus tard de prendre pour compte Win-Loss-Draw au lieu de simplement gagne si je choisis?

Modifier

Mon adaptation de la réponse TheSoftwareJedi:

private class RRWinRecord : IComparable
{
    public int Wins { get; set; }
    public int Losses { get; set; }
    public int Draws { get; set; }
    public double OverallScore { get; set; }
    public double WinRecord
    {
        get
        {
            return this.Wins * 1.0 + this.Draws * 0.5 + this.Losses * 0.0;
        }
    }

    public int CompareTo(object obj) { ... }

    public override bool Equals(object obj) { ... }
    public override int GetHashCode() { ... }
    public static bool operator ==(RRWinRecord lhs, RRWinRecord rhs) { ... }
    public static bool operator !=(RRWinRecord lhs, RRWinRecord rhs) { ... }
    public static bool operator >(RRWinRecord lhs, RRWinRecord rhs) { ... }
    public static bool operator <(RRWinRecord lhs, RRWinRecord rhs) { ... }
    public static bool operator >=(RRWinRecord lhs, RRWinRecord rhs) { ... }
    public static bool operator <=(RRWinRecord lhs, RRWinRecord rhs) { ... }
}

...

    int r = 1, lastRank = 1;
    RRWinRecord lastRecord = null;

    var ranks = from team in records.Keys
                let teamRecord = records[team]
                orderby teamRecord descending
                select new RRRank() { Team = team, Rank = r++, Record = teamRecord };

    foreach (var rank in ranks)
    {
        if (rank.Record != null && lastRecord == rank.Record)
        {
            rank.Rank = lastRank;
        }

        lastRecord = rank.Record;
        lastRank = rank.Rank;

        string scoreDescription = String.Format("{0}-{1}-{2}", rank.Record.Wins, rank.Record.Losses, rank.Record.Draws);
        yield return new TournamentRanking(rank.Team, rank.Rank, scoreDescription);
    }

    yield break;
Était-ce utile?

La solution

Cela devrait fonctionner pour un rang non dense:

static class Program
{

    static IEnumerable<Result> GetResults(Dictionary<TournamentTeam, double> wins, Dictionary<TournamentTeam, double> scores)
    {
        int r = 1;
        double lastWin = -1;
        double lastScore = -1;
        int lastRank = 1;

        foreach (var rank in from name in wins.Keys
                             let score = scores[name]
                             let win = wins[name]
                             orderby win descending, score descending
                             select new Result { Name = name, Rank = r++, Score = score, Win = win })
        {
            if (lastWin == rank.Win && lastScore == rank.Score)
            {
                rank.Rank = lastRank;
            }
            lastWin = rank.Win;
            lastScore = rank.Score;
            lastRank = rank.Rank;
            yield return rank;
        }
    }
}

class Result
{
    public TournamentTeam Name;
    public int Rank;
    public double Score;
    public double Win;
}

Autres conseils

Le classement est pas trop difficile. Juste méli-mélo OrderBy et Select modèles de mise en œuvre ensemble et vous pouvez avoir un outil facile à utiliser la méthode d'extension Classement. Comme ceci:

    public static IEnumerable<U> Rank<T, TKey, U>
    (
      this IEnumerable<T> source,
      Func<T, TKey> keySelector,
      Func<T, int, U> selector
    )
    {
        if (!source.Any())
        {
            yield break;
        }

        int itemCount = 0;
        T[] ordered = source.OrderBy(keySelector).ToArray();
        TKey previous = keySelector(ordered[0]);
        int rank = 1;
        foreach (T t in ordered)
        {
            itemCount += 1;
            TKey current = keySelector(t);
            if (!current.Equals(previous))
            {
                rank = itemCount;
            }
            yield return selector(t, rank);
            previous = current;
        }
    }

Voici un code de test

string[] myNames = new string[]
{ "Bob", "Mark", "John", "Jim", "Lisa", "Dave" };
//
var query = myNames.Rank(s => s.Length, (s, r) => new { s, r });
//
foreach (var x in query)
{
  Console.WriteLine("{0} {1}", x.r, x.s);
}

Ce qui donne ces résultats:

1 Bob
1 Jim
3 Mark
3 John
3 Lisa
3 Dave

En supposant que vous avez une structure List<Result> où l'objet Result a les paramètres suivants ...

Pesron     - string
Rank       - int
Wins       - double
TotalScore - int

Vous pouvez écrire un comparateur personnalisé, et ensuite passer à List.Sort(Comparison<Result> comparison) que

Alternative, vous pouvez simplement faire votre objet Result mettre en œuvre IComparable<Result> et le bâton dans votre classe.

        #region IComparable Members

        public int CompareTo(Result obj)
        {
            if (this.Rank.CompareTo(obj.Rank) != 0)
                return this.Rank.CompareTo(obj.Rank);

            if (this.Wins.CompareTo(obj.Wins) != 0)
                return (this.Wins.CompareTo(obj.Wins);

            return (this.TotalScore.CompareTo(obj.TotalScore) ;

        }

        #endregion

Ensuite, vous pouvez simplement appeler List<Result>.Sort();

Cela pourrait être un début:

Dictionary<TournamentTeam, double> wins = new Dictionary<TournamentTeam, double>();
Dictionary<TournamentTeam, double> score = new Dictionary<TournamentTeam, double>();
Dictionary<TournamentTeam, int> ranks = new Dictionary<TournamentTeam, int>();

int r = 1;

ranks = (
    from name 
    in wins.Keys 
    orderby wins[name] descending, scores[name] descending
    select new { Name = name, Rank = r++ })
    .ToDictionary(item => item.Name, item => item.Rank);

Je me rends compte que je suis en retard à la fête, mais je voulais prendre une photo de toute façon.

Voici une version qui utilise LINQ exclusivement:

private IEnumerable<TeamRank> GetRankings(Dictionary<TournamentTeam, double> wins, Dictionary<TournamentTeam, double> scores)
{
    var overallRank = 1;

    return
        from team in wins.Keys
        group team by new { Wins = wins[team], TotalScore = scores[team] } into rankGroup
        orderby rankGroup.Key.Wins descending, rankGroup.Key.TotalScore descending
        let currentRank = overallRank++
        from team in rankGroup
        select new TeamRank(team, currentRank, rankGroup.Key.Wins, rankGroup.Key.TotalScore);
}

Le type de retour:

public class TeamRank
{
    public TeamRank(TournamentTeam team, int rank, double wins, double totalScore)
    {
        this.Team = team;
        this.Rank = rank;
        this.Wins = wins;
        this.TotalScore = totalScore;
    }

    public TournamentTeam Team { get; private set; }

    public int Rank { get; private set; }

    public double Wins { get; private set; }

    public double TotalScore { get; private set; }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top