Как выбрать разных сотрудников с помощью LINQ на основе ID из коллекции employee, где зарплата сотрудника занимает 2-е место по величине?

#c# #.net #linq

#c# #.net #linq

Вопрос:

Посмотрите на приведенный ниже пример. Здесь идентификатор сотрудника = 2, он же Стив, имеет 3 дубликата. Мне нужна только одна запись каждого идентификатора. Я хочу выбрать запись, которая имеет 2-ю по величине зарплату. Итак, в случае Стива выбор будет либо одним из двух Стивов, у которых зарплата 160 в качестве зарплаты.

        public class Employee
        {
            public int ID { get; set; }
            public string Name { get; set; }
            public int Salary { get; set; }

            public static List<Employee> GetAllEmployees()
            {
                return new List<Employee>()
        {
            new Employee { ID = 1, Name = "Mark", Salary = 100 },
            new Employee { ID = 2, Name = "Steve", Salary = 150 },
            new Employee { ID = 2, Name = "Steve", Salary = 160 },
            new Employee { ID = 2, Name = "Steve", Salary = 160 },
            new Employee { ID = 2, Name = "Steve", Salary = 165 },
            new Employee { ID = 3, Name = "Ben", Salary = 140 }                

        };
            }
        }
  

Желаемый результат:

 1 Mark 100
1 Steve 160 //2nd highest salary of steve( there are two such steves so pick any)
1 Mark 100
1 Ben 140 
  

Я знаю, как получить разные записи на основе свойства:

 var result = Employee.GetAllEmployees().GroupBy(x=>x.ID).Distinct();
  

Но я заблудился в другой части.

Пожалуйста, обратите внимание, что я ищу только ответы на синтаксис LINQ lambda / extension. Спасибо!

Ответ №1:

Один из способов — использовать Select after GroupBy . Это преобразует каждую группу в сотрудника.

 Employee.GetAllEmployees().GroupBy(x=>x.ID).Select(x => FindSecondHighest(x));
  

где FindSecondHighest должно быть что-то вроде этого:

 private static Employee FindSecondHighest(IEnumerable<Employee> employees) {
    var list = employees.ToList();
    if (list.Count == 1) { return list[0]; }
    return list.OrderByDescending(x => x.Salary).Skip(1).First();
}
  

Вы можете переписать метод в лямбда-выражение, если хотите, но я чувствую, что так он более удобочитаем.

Редактировать:

Я понял, что на самом деле это не дает второй по величине зарплаты, если есть две самые высокие зарплаты. Чтобы фактически получить вторую по величине зарплату, вы можете использовать вторую GroupBy :

 private static Employee FindSecondHighest(IEnumerable<Employee> employees) {
    var list = employees.ToList();
    if (list.Count == 1) { return list[0]; }
    return list.GroupBy(x => x.Salary).OrderByDescending(x => x.Key).Skip(1).First();
}
  

Комментарии:

1. Спасибо. Ваш ответ определенно более удобочитаем, однако я отметил ответ Джеруна, поскольку он был первым, кто ответил правильным ответом.

Ответ №2:

Прежде всего, потеряйте Distinct() . Группировки по определению уникальны.

Согласно вашему требованию, вам нужно упорядочить результаты ваших группировок по зарплате (по убыванию) и взять вторую. Делайте это только тогда, когда в группе более 1 элемента. В результате этого это должно сработать:

     var result = Employee.GetAllEmployees()
        .GroupBy(x => x.ID)
        .Select(x => (x.Count() > 1 ? x.GroupBy(y => y.Salary).Skip(1).Select(y => y.First()) : x).First()
        );
  

Редактировать: обновил мой ответ на основе комментариев, нам нужно сгруппировать по зарплате, а затем пропустить 1, чтобы смягчить ситуацию, в которой вторая запись также имеет максимальное значение зарплаты.

Комментарии:

1. Вам не нужен порядок, если количество групп равно 1. Просто верните x

2. Как вы гарантируете, что вторая запись также имеет вторую максимальную зарплату? Потому что это может иметь то же значение, что и первая запись.

3. Хороший улов. Один из способов — сгруппировать по зарплате, затем пропустить 1. Смотрите мой обновленный ответ.

Ответ №3:

Должно сработать следующее:

 Employee.GetAllEmployees().Where(x => {
    recordsWithID = Employee.GetAllEmployees().Where(y => y.ID == x.ID).OrderByDescending(y => y.Salary);

    recordToReturn = recordsWithID.Count > 1 ? recordsWithID.Skip(1).First() : recordsWithID.First();

    return x.ID == recordToReturn.ID;
});
  

Сначала в главном предикате мы выбираем всех пользователей с идентификатором x. Затем мы получаем запись со второй по величине зарплатой, если существует более одной записи с этим идентификатором, в противном случае мы просто выбираем единственную запись с этим идентификатором. Затем, если для группы идентификаторов x x является фактически желаемой записью (таким образом, x имеет только одну запись со своим идентификатором или x имеет вторую по величине зарплату среди записей с идентификатором x), x возвращается, в противном случае это не так.

Я не могу протестировать это в настоящее время, поскольку я не перед компьютером, но это должно дать вам представление.

Ответ №4:

Вы можете попробовать что-то подобное, используя .GroupBy и .Select :

 static void Main(string[] args)
{
    List<Employee> secondHighestSalaryPersons = Employee.GetAllEmployees()
         .GroupBy(x => x.Name)
         .Select(x =>
         {
             var group = x.ToList();

             if (group.Count > 1)
                 return group.OrderByDescending(y => y.Salary).Skip(1).FirstOrDefault();
             else
                 return group.FirstOrDefault();
         })
         .ToList();
}