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