Linq List Ranking in C#

*爱你&永不变心* 提交于 2019-12-11 05:14:26

问题


I have the below list which consist of a list of names and scores. The scores I got from using linq to get the sum of points for the list of peope:

  Name  Score
   Ed    5
   John  6
   Sara  7
   Dean  3
   Nora  4

So i was able to group the list and make the sums, but now I am trying to rank these based on the score. So I am trying to get the below list:

Rank Name Score
 1   Sara  7
 2   John  6
 3   Ed    5
 4   Nora  4
 5   Dean  3

But the linq code I am using doesn't seem to be getting the right ranks for me.

Here's my code for making the list and making the sums and ranking:

    public class Person
    {

        public int Rank { get; set; }
        public string Name { get; private set; }
        public int Score { get; set; }

        public Person(int rank, string name, int score)
        {
            Rank = rank;
            Name = name;
            Score = score;

        }
    }

    public ObservableCollection<Person> Persons { get; set; }
    public MainWindow()
    {            
 Persons = new ObservableCollection<Person>();
        var data = lines.Select(line => {
        var column = line.Split(',');
        var name = column[0];
        int score = int.Parse(column[1]);
        int rank =0; 
        return new { name, score, rank };
    });

        var groupedData = data.OrderByDescending(x => x.score).GroupBy(p => p.name).Select((g, i) => new { name = g.Key, rank=i+1, score = g.Sum(p => p.score)});

    var persons = groupedData.Select(p => new Person(p.rank, p.name, p.score));

    foreach (var person in persons) {
        Persons.Add(person);
    }
    datagrid.ItemsSource = Persons;
}

I just would like to know how to include correct ranks in linq.


回答1:


You have to OrderByDescending the groupedData (which has a total score) rather than the raw data containing non-summed, individual scores.

var persons = groupedData.Select(p => new Person(p.rank, p.name, p.score)).OrderByDescending(x => x.Score);

Edit: (putting the correct rank into Person.Rank)

var groupedData = data.GroupBy(p => p.name).Select(g => new { name = g.Key, score = g.Sum(p => p.score)}).OrderByDescending(x => x.score);
var persons = groupedData.Select((p,i) => new Person(i+1, p.name, p.score));



回答2:


I asked a similar question to this a while back. My problem was that if people have the same score it will assign the wrong rank to the person. With your current code if John also had a score of 7 he would be ranked #2 as you are ordering by score and just using its position in the list when hes really tied for #1.

Here was my question and solution: Get the index of item in list based on value




回答3:


How about an extension method doing ranking:

public static IEnumerable<TResult> SelectRank<TSource, TValue, TResult>(this IEnumerable<TSource> source, 
     Func<TSource, TValue> valueSelector, 
     Func<TSource, int, TResult> selector)
            where TValue : struct
        {
            TValue? previousValue = default(TValue?);
            var count = 0;
            var rank = 0;
            foreach (var item in source)
            {
                count++;
                var value = valueSelector(item);
                if (!value.Equals(previousValue))
                {
                    rank = count;
                    previousValue = value;
                }
                yield return selector(item, rank);
            }
        }

Usage profiles.SelectRank(x => x.Score, (x, r) => new { x.Name, Rank = r }). This method do only 1 iteration of the source. I do recommend to use a sorted source parameter.



来源:https://stackoverflow.com/questions/45838787/linq-list-ranking-in-c-sharp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!