C# Ranking of objects, multiple criteria

前端 未结 5 865
日久生厌
日久生厌 2020-12-05 12:26

I am building a plugin for a LAN party website that I wrote that would allow the use of a Round Robin tournament.

All is going well, but I have some questions about

相关标签:
5条回答
  • 2020-12-05 12:54

    Assuming you have a List<Result> structure where the Result object has the following parameters...

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

    You could write a custom comparer, and then pass that to List.Sort(Comparison<Result> comparison)

    Alternative, you could just make your Result object implement IComparable<Result> and stick this in your class.

            #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
    

    Then you can just call List<Result>.Sort();

    0 讨论(0)
  • 2020-12-05 13:00

    I realize I'm late to the party, but I wanted to take a shot anyhow.

    Here is a version which uses LINQ exclusively:

    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);
    }
    

    The return type:

    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; }
    }
    
    0 讨论(0)
  • 2020-12-05 13:07

    This should work for a non-dense rank:

    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;
    }
    
    0 讨论(0)
  • 2020-12-05 13:10

    Ranking isn't too hard. Just mishmash OrderBy and Select implementation patterns together and you can have an easy to use Ranking extension method. Like this:

        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;
            }
        }
    

    Here's some test code

    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);
    }
    

    Which yields these results:

    1 Bob
    1 Jim
    3 Mark
    3 John
    3 Lisa
    3 Dave
    
    0 讨论(0)
  • 2020-12-05 13:16

    This could be a start:

    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);
    
    0 讨论(0)
提交回复
热议问题