tesis sistemas sistema recomendacion hacer como c# linq linq-to-objects ranking

c# - sistemas - sistema de recomendacion tesis



C#Ranking de objetos, mĂșltiples criterios (5)

Estoy creando un complemento para un sitio web de LAN que escribí y que permitiría el uso de un torneo de Round Robin.

Todo va bien, pero tengo algunas preguntas sobre la manera más eficiente de clasificar dos criterios.

Básicamente, me gustaría el siguiente diseño de clasificación:

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

En el servidor SQL, usaría:

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

Ahora, solo tengo Lista, Diccionario y etc. para trabajar con

Específicamente:

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

¿Hay alguna manera de hacer este estilo de clasificación con LINQ?

Si no, ¿existe una forma extensible que me permita luego tomar en cuenta Win-Loss-Draw en lugar de simplemente ganar si así lo deseo?

Editar:

Mi adaptación de la respuesta de 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;


Esto debería funcionar para un rango no denso:

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


Esto podría ser un comienzo:

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


Me doy cuenta de que llego tarde a la fiesta, pero de todos modos quería tomar una foto.

Aquí hay una versión que usa LINQ exclusivamente:

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

El tipo de devolución:

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


Suponiendo que tiene una estructura List<Result> donde el objeto Result tiene los siguientes parámetros ...

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

Puede escribir un comparador personalizado y luego pasarlo a List.Sort(Comparison<Result> comparison)

Alternativa, puede hacer que su objeto Result implemente IComparable<Result> y pegar esto en su clase.

#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

Luego puede llamar a List<Result>.Sort() ;


La clasificación no es muy difícil. Solo mezcle ordenar por orden y seleccionar patrones de implementación juntos y puede tener un método de extensión de clasificación fácil de usar. Me gusta esto:

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

Aquí hay un código de prueba

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

Que produce estos resultados:

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