C# 개체 순위, 다중 기준
-
06-09-2019 - |
문제
라운드 로빈 토너먼트를 사용할 수있는 LAN 파티 웹 사이트 용 플러그인을 작성하고 있습니다.
모든 것이 잘 진행되고 있지만 두 가지 기준에 따라 가장 효율적인 방법에 대한 질문이 있습니다.
기본적으로 다음 순위 레이아웃을 원합니다.
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;
해결책
이것은 비 밀도 순위에 대해 효과가 있어야합니다.
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;
}
다른 팁
순위는 너무 어렵지 않습니다. Mishmash Orderby만으로도 구현 패턴을 함께 선택하면 사용하기 쉬운 순위 확장 방법을 가질 수 있습니다. 이와 같이:
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
당신이 있다고 가정합니다 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()
;
이것은 시작 일 수 있습니다.
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);
나는 파티에 늦었다는 것을 알고 있지만 어쨌든 총을 맞이하고 싶었습니다.
다음은 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; }
}