C#中的LINQ列表排名[英] Linq List Ranking in C#

本文是小编为大家收集整理的关于C#中的LINQ列表排名的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我有以下列表,其中包括名称和分数列表.我从使用Linq获得的分数来获取Peope列表的积分总和:

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

因此,我能够对列表进行分组并制作汇总,但是现在我试图根据分数对这些列表进行排名.因此,我正在尝试获取以下列表:

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

但是我使用的LINQ代码似乎并没有为我获得正确的排名.

这是我列出列表并进行总和和排名的代码:

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

我只是想知道如何在linq中包括正确的等级.

推荐答案

您必须OrderByDescending groupedData(具有总分),而不是包含未命中的单个分数的原始数据.

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

edit :(将正确的等级放入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));

其他推荐答案

我不久前问了一个类似的问题.我的问题是,如果人们的分数相同,它将为人分配错误的等级.使用您当前的代码,如果John的得分为7,他将按得分订购时排名第二,并且仅在列表中使用其#1的位置.

.

这是我的问题和解决方案:list--------------------------------------基于值

的列表中的项目索引

其他推荐答案

进行排名的扩展方法如何:

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

用法profiles.SelectRank(x => x.Score, (x, r) => new { x.Name, Rank = r }).此方法仅做1个源.我确实建议使用分类source参数.

本文地址:https://www.itbaoku.cn/post/1556961.html

问题描述

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.

推荐答案

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

其他推荐答案

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

其他推荐答案

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.