问题
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