2010-03-05 2 views
2

J'ai un objet dans une liste dont j'ai besoin de classer de différentes manières. Actuellement, le code est plutôt lourd car il nécessite que j'adresse individuellement chaque colonne. Exemple:Traitement des données de classement w/C# & LINQ

public class Data 
{ 
    public int AValue { get; set; } 
    public int ARanking { get; set; } 
    public int BValue { get; set; } 
    public int BRanking { get; set; } 
    public int CValue { get; set; } 
    public int CRanking { get; set; } 
} 

public class Container 
{ 
    public List<Data> RankingData { get; set; } 

    public void RankData() 
    { 
     int count = 1; 

     foreach (Data item in RankingData.OrderBy(d => d.AValue)) 
     { 
      item.ARanking = count; 
      count++; 
     } 

     count = 1; 

     foreach (Data item in RankingData.OrderBy(d => d.BValue)) 
     { 
      item.BRanking = count; 
      count++; 
     } 

     count = 1; 

     foreach (Data item in RankingData.OrderBy(d => d.CValue)) 
     { 
      item.CRanking = count; 
      count++; 
     } 
    } 
} 

Le problème que je tente de résoudre est que je veux écrire quelque chose à peu près comme ceci:

public void RankData<V, R>() 
{ 
    int count = 1; 

    foreach(Data item in RankingData.OrderBy(V)) 
    { 
     item.R = count; 
     count++; 
    } 
} 

Alors que je dois modifier la logique de classement (par exemple, la poignée départage règles) que j'écris le code une fois au lieu de copier le code 20 fois pour faire correspondre les règles. Qu'est-ce que je rate?

MISE À JOUR

En utilisant la solution de Tanzelax comme base c'est la classe d'extension, je suis venu avec:

public static class RankingExtension 
{ 
    public static void SetRanking<TKey>(this List<Data> dataSet, bool Ascending, Func<Data, TKey> getOrderBy, Action<Data, int> setRank) 
     where TKey : IComparable 
    { 
     var ordered = (Ascending) ? dataSet.OrderBy(getOrderBy) : dataSet.OrderByDescending(getOrderBy); 

     int i = 1; 
     foreach (Data item in ordered) 
     { 
      setRank(item, i); 
      i++; 
     } 
    } 
} 

je devais ajouter un interrupteur pour que je puisse contrôler si oui ou non la le champ était trié ascendant ou non. Et dans mes scénarios de test, il produit la sortie appropriée:

List<Data> test = new List<Data>(); 
    test.Add(new Data { AValue = 25, BValue = 1.25, CValue = 99.99 }); 
    test.Add(new Data { AValue = 89, BValue = 2.10, CValue = 1.01 }); 
    test.Add(new Data { AValue = 10, BValue = 6, CValue = 45.45 }); 
    test.Add(new Data { AValue = 15, BValue = 2.33, CValue = 2.99 }); 
    test.Add(new Data { AValue = 90, BValue = 5.43, CValue = 27.89 }); 

    test.SetRanking(false, d => d.AValue, (d, i) => d.ARank = i); 
    test.SetRanking(false, d => d.BValue, (d, i) => d.BRank = i); 
    test.SetRanking(true, d => d.CValue, (d, i) => d.CRank = i); 

Répondre

1

Non testé, mais quelque chose comme ceci:

void SetRanking(this List<Data> dataSet, Func<Data,int> getOrderBy, Action<Data,int> setRank) 
{ 
    var ordered = dataSet.OrderBy(getOrderBy).ToArray(); 

    int i = i; 
    foreach (Data item in ordered) 
    { 
     setRank(item, i); 
     i++; 
    } 
} 

RankingData.SetRanking(d => d.AValue, (d,i) => d.ARanking = i); 
RankingData.SetRanking(d => d.BValue, (d,i) => d.BRanking = i); 
RankingData.SetRanking(d => d.CValue, (d,i) => d.CRanking = i); 
+0

Votre code n'était pas tout à fait là, mais il m'a fait sacrément fermer. Je posterai ma modification dans un peu. – thaBadDawg

+0

@thaBadDawg: Content que je puisse aider. :) Pourrait également générique sur les données et autres joyeusetés. De plus, au lieu de passer un booléen ascendant, vous pourriez simplement passer CValue négatif dans orderBy lambda, bien que je suis sûr que beaucoup de gens s'opposeraient à cette approche. – Tanzelax

0

passe dans un Func<Data,K> qui retourne la clé de classement. K devrait implémenter IComparable

public static void Rank<K>(IEnumerable<Data> source, Func<Data,K> rankBy) where K : IComparable 
{ 
    int count = 1; 
    foreach (var item in source.OrderBy(rankBy)) 
    { 
     item.R = count; 
     ++count; 
    } 
} 
1

Ceci est similaire à la réponse de Tanzelax mais est une méthode d'extension générique.

public static void RankData<TSource, TKey>(
    this IEnumerable<TSource> source, 
    Func<TSource, TKey> keySelector, 
    Action<TSource, int> rankSetter 
) 
{ 
    int count = 1; 
    foreach (var item in source.OrderBy(keySelector)) 
    { 
     rankSetter(item, count); 
     ++count; 
    } 
} 

Il serait appelé similaire à la réponse de Tanzelax également.

RankingData.RankData(d => d.AValue, (d,i) => d.ARanking = i);