monty
monty

Reputation: 8755

Linq for calculating rank with group and orderby

I am trying to calculate the rank of objects. Since multiple objects can have the same score I would need to group them so they get the same rank.

public class RankedItem {

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

  public RankedItem(string name, int score) {
     Name = name;
     Score = score;
  }

}

public void CalculateRanks() {

  List<RankedItem> items = new List<RankedItems> {
    new RankedItem("A", 3),
    new RankedItem("B", 2),
    new RankedItem("C", 3),
    new RankedItem("D", 1),
    new RankedItem("E", 2)
  };
 
  IOrderedEnumerable<IGrouping<int, RankedItems>> rankedItems = items.GroupBy(i => b.Score).OrderBy(g => g.Key);

  
}

How can I set the rank now, so that rank 1 will be assigned to the highest score?

Upvotes: 2

Views: 867

Answers (1)

Mong Zhu
Mong Zhu

Reputation: 23732

You have basically 2 options here. Linq or Loop. For both options you should use OrderByDescending since your score rank relationship is inverse. Then you can use the index + 1 to assign the ranks.

  1. Loop.

for this option you need a collection to hold your groupings which can be iterated using the index [ ] operator. This is not possible in IOrderedEnumerable. So I suggest to use a List:

List<IGrouping<int, RankedItem>> rankedItems = items.GroupBy(b => b.Score)
                                                    .OrderByDescending(g => g.Key)
                                                    .ToList();

Now you can simply loop through the list having the index and loop again through all elements of each group to use the index to assign the rank:

for (int i = 0; i < rankedItems.Count(); i++)
{
    IGrouping<int, RankedItem> grouping = rankedItems[i];
    foreach (var element in grouping)
    {
        element.Rank = i + 1;
    }
}
  1. LINQ:

Use the index in this Select overload statement and create new objects using your constructor:

List<RankedItem> rankedItems = items.GroupBy(b => b.Score)
                                    .OrderByDescending(g => g.Key)
                                    .SelectMany((item, index) => item.Select(inner => 
                                            new RankedItem(inner.Name, item.Key) {Rank = index + 1})
                                    ).ToList();

Outcome:

enter image description here

Upvotes: 4

Related Questions