1

I have a class, let's say as below:

  public class Customer{
      public string Name;
      public int Score;
  }

So, I have a list of customers. Customers may have same name with different Scores. Now I want to get the customer with the maximum Score if the name is same.

For example:

var customers = new List<Customer>()
{
    new Customer() { Name = "Rahim", Score = 30 },
    new Customer() { Name = "Rahim", Score = 25 },
    new Customer() { Name = "Karim", Score = 49 },
    new Customer() { Name = "Aziz", Score = 24 },
};

The output should be,

Rahim 30
Karim 49
Aziz 24
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
Rashedul.Rubel
  • 3,446
  • 25
  • 36

3 Answers3

2

Use the following query:

var customers = customerList
    .GroupBy(x => x.Name)
    .Select(g => g.OrderByDescending(x => x.Score).First())
    .ToList();
Svyatoslav Danyliv
  • 21,911
  • 3
  • 16
  • 32
  • 1
    A good habit that helps with filtering and refactoring is to do this: `.SelectMany(g => g.OrderByDescending(x => x.Score).Take(1))` – Enigmativity Mar 24 '22 at 06:52
  • 1
    I do not see any problems with filtering and refactoring. Can you show example? – Svyatoslav Danyliv Mar 24 '22 at 06:58
  • 2
    For example, making this change may cause an exception: `.Select(g => g.Where(x => x.Score > 70).OrderByDescending(x => x.Score).First())`. However, it works fine on `.SelectMany(g => g.Where(x => x.Score > 70).OrderByDescending(x => x.Score).Take(1))` – Enigmativity Mar 24 '22 at 07:14
1

It's nice and simple with the MaxBy operator:

var customers = new List<Customer>()
{
    new Customer() { Name = "Rahim", Score = 30 },
    new Customer() { Name = "Rahim", Score = 25 },
    new Customer() { Name = "Karim", Score = 49 },
    new Customer() { Name = "Aziz", Score = 24 },
};

Using .NET 6.0:

List<Customer> output =
    customers
        .GroupBy(c => c.Name)
        .Select(c => c.MaxBy(x => x.Score))
        .ToList();

Using Microsoft's Interactive Framework (System.Interactive):

List<Customer> output =
    customers
        .GroupBy(c => c.Name)
        .SelectMany(c => c.MaxBy(x => x.Score))
        .ToList();

That gives me:

output

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
  • 1
    I agree with this. But I think you meant to use `.Select()` rather than `SelectMany()`? – Astrid E. Mar 24 '22 at 07:15
  • @AstridE. - No. I meant to use `SelectMany`. It would have a syntax error with `Select`. `MaxBy` returns and `IList` as there may be zero (in the case of an empty source list) or one or many (in the case of ties) results. – Enigmativity Mar 24 '22 at 07:23
  • 1
    Are we talking about the same [`.MaxBy()`](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.maxby?view=net-6.0)? It returns an object of type `TSource`; in this case, a `Customer`. – Astrid E. Mar 24 '22 at 07:30
  • @AstridE. - Good catch. I was using Microsoft's System.Interactive library which has the more sensible `MaxBy`. – Enigmativity Mar 24 '22 at 07:33
  • Interesting, I was not familiar with that one. My bad, making assumptions that you were using `System.Linq`. :) – Astrid E. Mar 24 '22 at 07:36
  • @AstridE. - It's brought to us by the same team that did the Reactive Framework. They were a bunch of smart guys. I wonder who designed the new `MaxBy`? – Enigmativity Mar 24 '22 at 08:45
  • 2
    @Enigmativity the Ix `MaxBy` API has been renamed `MaxByWithTies`. See [.NET 6.0 Enumerable.MaxBy conflicts with Enumerable.MaxBy](https://github.com/dotnet/reactive/issues/1647). – Theodor Zoulias Mar 25 '22 at 11:14
-2

Using linq, we need to group by name first and then apply SelectMany to retrieve the highest score from each group as below:

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    class HelloWorld {
      static void Main() {   
          var customerList = new List<Customer>{
              new Customer {Name = "a", Score =30},
               new Customer {Name = "a", Score =35},
                new Customer {Name = "b", Score =20},
                 new Customer {Name = "c", Score =50},
                  new Customer {Name = "b", Score =60},
                   new Customer {Name = "a", Score =80},
          };

    List<Customer> customers = customerList
    .GroupBy(t => t.Name)
    .SelectMany(a => a.Where(b => b.Score == a.Max(c => c.Score))).ToList();
                
                foreach(var data in customers){
                    Console.WriteLine(data.Name +","+ data.Score);
                }   
                Console.ReadLine();
      }
      
      public class Customer{
          public string Name;
          public int Score;
      }
Rashedul.Rubel
  • 3,446
  • 25
  • 36
  • 1
    This is running at `O(n^2)` complexity when it could be `O(n)`. – Enigmativity Mar 24 '22 at 06:51
  • 1
    Down-votes do not indicate failure. They indicate that this is an answer I wouldn't recommend using. "Voting down, also known as "casting downvotes", is how the community indicates which questions and answers are least useful." – Enigmativity Mar 24 '22 at 07:10