2009-05-31 70 views
9

我正在为LAN party网站构建一个插件,我写了允许使用循环赛的比赛。C#对象的排名,多个条件

一切进展顺利,但我有一些关于排名超过两个标准的最有效方法的问题。

基本上,我想下面的评级布局:

  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 

在SQL Server中,我会用:

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

现在,我只有列表,字典,等工作与

具体来说:

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

有没有办法用LINQ来做这种排序方式?

如果没有,是否有一个扩展的方式,让我以后采取占赢取损失,绘制,而不是仅仅赢,如果我选择?

编辑:

我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; 
+0

感谢所有伟大的答案的伙计们! – 2009-06-02 05:23:41

回答

2

这应该非密集秩工作:

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

这可能是一个开始:

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

这是一个很好的开始。我会看看如果我不能添加领带的情况 – TheSoftwareJedi 2009-05-31 16:06:13

+0

是的,好的开始+1。但是,我需要非密集的排名。我构建了一个NUnit测试来定位所需的行为。很快会回来查看。 – 2009-05-31 16:18:34

+0

在下面添加了非密集解决方案。只是编辑这个,但我认为它是如此不同,它需要一个新的答案。 – TheSoftwareJedi 2009-05-31 16:26:50

3

假设你有一个List<Result>结构,其中Result对象具有以下参数...

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

你可以写一个自定义比较,然后传递到List.Sort(Comparison<Result> comparison)

替代,你可以只让你Result对象实施IComparable<Result> 和类坚持这一点。

 #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 

然后您可以拨打List<Result>.Sort();

+0

不明白为什么这是被拒绝的。您可以简单地使用自己的公式来实现自己的比较器,以确定两个对象的相等性。任何公式都可以做! – 2009-05-31 16:40:06

+0

谢谢eric。想知道我自己是一样的 – 2009-05-31 16:46:36

1

我意识到我迟到了,但我想采取了一枪进不去。

下面是使用LINQ专门版本:

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

返回类型:

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

排名不是太难。只需mishmash OrderBy和Select实现模式在一起,你可以有一个易于使用的排名扩展方法。就像这样:

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

下面是一些测试代码

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

其中产量这些结果:

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