简体   繁体   English

如何根据员工收入中员工薪水排名第二的基于身份证的LINQ选择不同的员工?

[英]How to select distinct employees with LINQ based on ID from an employee collection where employee 's salary is 2nd highest?

Look at the below example. 请看下面的例子。 Here Employee ID =2 aka Steve has 3 duplicates. 这里员工ID = 2又名史蒂夫有3个重复。 I want only one record of each ID. 我只想要每个ID的一条记录。 I want to pick the record which has 2nd highest salary. 我想选择薪水第二高的记录。 So in case of Steve the choice will be either one of the two Steves hat have 160 as Salary. 因此,在史蒂夫的情况下,选择将是两个史蒂夫帽子中的一个有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 }                

        };
            }
        }

Desired Output: 期望的输出:

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 

I know how to get distinct records based on a property: 我知道如何根据属性获取不同的记录:

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

But I am lost on the other part. 但我在另一方面迷失了方向。

Please note I am looking for LINQ lambda/extension syntax answers only. 请注意我只是在寻找LINQ lambda /扩展语法的答案。 Thanks! 谢谢!

One way is to use Select after GroupBy . 一种方法是在GroupBy之后使用Select This will transform each group into an employee. 这将把每个小组变成一个雇员。

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

where FindSecondHighest should be something like this: 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();
}

You can rewrite the method to a lambda if you like, but I feel like it's more readable this way. 如果你愿意,你可以将方法重写为lambda,但我觉得这样的方式更具可读性。

EDIT: 编辑:

I realised that this doesn't actually get the second highest salary if there are two highest salaries. 我意识到,如果有两个最高的工资,这实际上并没有获得第二高薪。 To actually get the second highest salary, you can use a second GroupBy : 要实际获得第二高薪,您可以使用第二个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();
}

First of all, lose the Distinct() . 首先,失去Distinct() The groupings are by definition unique. 根据定义,分组是唯一的。

As per your requirement, you need to order the results of your groupings by salary (descending) and take the second one. 根据您的要求,您需要按工资(降序)订购分组结果,然后选择第二个。 Do this only when the grouping has more than 1 item. 仅在分组具有多个项目时执行此操作。 Resulting from that, this should work: 由此产生的结果应该是:

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

Edit: updated my answer based on comments, we need to group by salary and then skip 1 to mitigate the situation in which the second record also has the top salary value. 编辑:根据评论更新我的答案,我们需要按工资分组,然后跳过1以缓解第二条记录也具有最高薪水值的情况。

The following should work: 以下应该有效:

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

First, in the main predicate, we select all users with x's ID. 首先,在主谓词中,我们选择具有x的ID的所有用户。 Then, we get the record with the second highest Salary, if there are more than one records with that ID, otherwise, we just select the only record with that ID. 然后,如果有多个具有该ID的记录,我们获得具有第二高薪水的记录,否则,我们只选择具有该ID的唯一记录。 Then, if for x's ID group, x is the actually desired record (so x has the only one with its ID or x has the second highest salary among records with x's ID), x is returned, otherwise it is not. 然后,如果对于x的ID组,x是实际所需的记录(因此x只有一个带有其ID或x在具有x的ID的记录中具有第二高的薪水),则返回x,否则不返回。

I cannot test this currently as I am not in front of a PC, but this should give you an idea. 我目前无法测试这个,因为我不在电脑前,但这应该给你一个想法。

You can try something like this using .GroupBy and .Select : 您可以使用.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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM