C # ترتيب الكائنات، معايير متعددة
-
06-09-2019 - |
سؤال
أنا أقوم ببناء مكون إضافي لموقع Lan Party الإلكتروني الذي كتبته من شأنه أن يسمح باستخدام بطولة Robin Round.
كل شيء يسير على ما يرام، لكن لدي بعض الأسئلة حول الطريقة الأكثر كفاءة لتحتل معيارين.
في الأساس، أود تخطيط الترتيب التالي:
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>();
هل هناك طريقة للقيام بهذا النمط من الترتيب مع لينك؟
إذا لم يكن كذلك، هل هناك توسع الطريقة التي من شأنها أن تسمح لي لاحقا أن تأخذ لحساب السحب الفوز بدلا من فوز فقط إذا اخترت ذلك؟
يحرر:
بلدي التكيف عن إجابة 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; }
}