使用LINQ对列表及其嵌套对象进行排序。

10

有一个组织,有几个部门,每个部门都有一些员工。

我创建了以下对象模型:

public class Organisation
{
    public int Code { get; set; }
    public string Type { get; set; }
    public string Name { get; set; }
    public List<Department> Departments { get; set; }
}

public class Department
{
    public int Code { get; set; }
    public string Name { get; set; }
    public List<Employee> Employees { get; set; }
}

public class Employee
{
    public int Code { get; set; }
    public string Name { get; set; }
}

现在,我有一个这些组织的列表,并且使用LINQ,我想按照以下方式对输出进行排序/排序:

1)组织:按代码和名称排序

2)部门:按代码和名称排序

3)员工:按代码和名称排序

下面是我填充的一些测试数据:

var britishTelecomLtd = new Organisation
            {
                Code = 8,
                Name = "British Telecom Ltd",
                Type = "Institutional",
                Departments =  new List<Department>
                    {
                        new Department
                            {
                                Code = 6,
                                Name = "Finance",
                                Employees = new List<Employee>
                                    {
                                        new Employee
                                            {
                                                Code = 5,
                                                Name = "Peter"
                                            },
                                            new Employee
                                            {
                                                Code = 2,
                                                Name = "James"
                                            },
                                            new Employee
                                            {
                                                Code = 6,
                                                Name = "Andrew"
                                            }
                                    }
                            },
                            new Department
                            {
                                Code = 5,
                                Name = "Accounts",
                                Employees = new List<Employee>
                                    {
                                        new Employee
                                            {
                                                Code = 15,
                                                Name = "Jane"
                                            },
                                            new Employee
                                            {
                                                Code = 22,
                                                Name = "John"
                                            },
                                            new Employee
                                            {
                                                Code = 16,
                                                Name = "Mark"
                                            }
                                    }
                            }

                    } 

            };

        var virginMediaLtd = new Organisation
        {
            Code = 5,
            Name = "Virgin Media Ltd",
            Type = "Institutional",
            Departments = new List<Department>
                    {
                        new Department
                            {
                                Code = 6,
                                Name = "Sales",
                                Employees = new List<Employee>
                                    {
                                       new Employee
                                            {
                                                Code = 5,
                                                Name = "Peter"
                                            },
                                            new Employee
                                            {
                                                Code = 2,
                                                Name = "James"
                                            },
                                            new Employee
                                            {
                                                Code = 6,
                                                Name = "Andrew"
                                            }
                                    }
                            },
                            new Department
                            {
                                Code = 5,
                                Name = "Support",
                                Employees = new List<Employee>
                                    {
                                        new Employee
                                            {
                                                Code = 15,
                                                Name = "Jane"
                                            },
                                            new Employee
                                            {
                                                Code = 22,
                                                Name = "John"
                                            },
                                            new Employee
                                            {
                                                Code = 16,
                                                Name = "Mark"
                                            }
                                    }
                            }

                    }

        };

        var pcWorldLtd = new Organisation
        {
            Code = 18,
            Name = "PC World Ltd",
            Type = "Retail",
            Departments = new List<Department>
                    {
                        new Department
                            {
                                Code = 6,
                                Name = "Marketing",
                                Employees = new List<Employee>
                                    {
                                          new Employee
                                            {
                                                Code = 15,
                                                Name = "Jane"
                                            },
                                            new Employee
                                            {
                                                Code = 22,
                                                Name = "John"
                                            },
                                            new Employee
                                            {
                                                Code = 16,
                                                Name = "Mark"
                                            }
                                    }
                            },
                            new Department
                            {
                                Code = 5,
                                Name = "Customer Services",
                                Employees = new List<Employee>
                                    {
                                         new Employee
                                            {
                                                Code = 5,
                                                Name = "Kelly"
                                            },
                                            new Employee
                                            {
                                                Code = 2,
                                                Name = "Jenny"
                                            },
                                            new Employee
                                            {
                                                Code = 6,
                                                Name = "Tricia"
                                            }
                                    }
                            }

                    }

        };

        var blueCatLtd = new Organisation
            {
                Code = 3,
                Name = "Blue Cat Music Ltd",
                Type = "Retail",
                Departments = new List<Department>
                    {
                        new Department
                            {
                                Code = 6,
                                Name = "Sales",
                                Employees = new List<Employee>
                                    {
                                         new Employee
                                            {
                                                Code = 5,
                                                Name = "Peter"
                                            },
                                            new Employee
                                            {
                                                Code = 2,
                                                Name = "James"
                                            },
                                            new Employee
                                            {
                                                Code = 6,
                                                Name = "Andrew"
                                            }
                                    }
                            },
                        new Department
                            {
                                Code = 5,
                                Name = "Warehouse",
                                Employees = new List<Employee>
                                    {
                                        new Employee
                                            {
                                                Code = 5,
                                                Name = "Andy"
                                            },
                                        new Employee
                                            {
                                                Code = 2,
                                                Name = "Robert"
                                            },
                                        new Employee
                                            {
                                                Code = 6,
                                                Name = "Dave"
                                            }
                                    }
                            }

                    }
            };

        var organisations = new List<Organisation>
            {
                britishTelecomLtd,
                virginMediaLtd,
                pcWorldLtd,
                blueCatLtd
            };

我在这里将数据添加到字典中:

   var legalEntitiesCollectionByType = new Dictionary<string, ICollection<Organisation>>
            {
                {
                    "Institutional", organisations
                        .Where(x => x.Type == "Institutional")
                        .OrderBy(x => x.Code).ThenBy(x => x.Name)
                        .ToList()
                },
                {
                    "Retail", organisations
                        .Where(x => x.Type == "Retail")
                        .OrderBy(x => x.Code).ThenBy(x => x.Name)
                        .ToList()
                }
            };

按照这种方式做,排序只在组织级别而不是部门或员工级别上发生。

我的问题是,在填充上述字典的同时如何实现在所有3个级别上进行排序?

干杯


4
在填充字典时不需要排序,只有在查询时才需要进行排序。 - Mister Epic
2
如果必须使用这种方法,那么您需要重新创建每个对象并在此过程中对元素进行排序。例如: .OrderBy(x => x.Code).ThenBy(x => x.Name).Select(o => new Organisation() { //populate with data from o, using OrderBy when needed });。但是您应该重新考虑您的设计。 - BartoszKP
@ChrisHardie 无法做到这一点。客户需要一个排序过的字典。 - haroonxml
@BartoszKP 我会尝试你的建议。 - haroonxml
5个回答

13

我知道这是一个老问题,但有一种更简单的方法可以实现相同的结果:

organisations = organisations.OrderBy(org =>
{
   org.Departments = org.Departments
   .OrderBy(dept =>
   {
     dept.Employees = dept.Employees
                     .OrderBy(employee => employee.Code)
                     .ThenBy(employee=>employee.Name);
     return dept.Code;
   })
   .ThenBy(dept=>dept.Name);

   return org.Code;
})
.ThenBy(org=>org.Name); 

在我看来,这是最好的答案。有一个错别字——Departmets 应该改为 Departments,每个“...ThenBy(...)”都需要以“.ToList();”结尾。 - Tom Baxter
谢谢指出笔误。至于.ToList(),我认为它并不是必要的。我会检查并告诉你的。 - Hellraiser

6
你需要在返回的对象中进行三个级别的排序,就像这样(我只展示“Retail”,“Institutional”也需要按相同方式进行排序):
{
"Retail", organisations
    .Where(x => x.Type == "Retail")
    .OrderBy(x => x.Code).ThenBy(x => x.Name)
    .Select(x => new Organisation {
        x.Code
    ,   x.Type
    ,   x.Name
    ,   Departments = x.Departmentsd.OrderBy(d => d.Code).ThenBy(d => d.Name)
        .Select(d => new Department {
            d.Code
        ,   d.Name
        ,   Employees = d.Employees.OrderBy(e => e.Code).ThenBy(e => e.Name).ToList()
        })
    }).ToList()
}

由于您需要多次选择此选项,因此建议将此代码封装成一个方法,并在需要的地方使用它,像下面这样:

private Organisation SortedOrganisation(Organisation x) {
    return new Organisation {
        x.Code
    ,   x.Type
    ,   x.Name
    ,   Departments = x.Departmentsd.OrderBy(d => d.Code).ThenBy(d => d.Name)
        .Select(d => new Department {
            d.Code
        ,   d.Name
        ,   Employees = d.Employees.OrderBy(e => e.Code).ThenBy(e => e.Name).ToList()
        })
    };
}

...

var legalEntitiesCollectionByType = new Dictionary<string, ICollection<Organisation>>
        {
            {
                "Institutional", organisations
                    .Where(x => x.Type == "Institutional")
                    .OrderBy(x => x.Code).ThenBy(x => x.Name)
                    .Select(SortedOrganisation)
                    .ToList()
            },
            {
                "Retail", organisations
                    .Where(x => x.Type == "Retail")
                    .OrderBy(x => x.Code).ThenBy(x => x.Name)
                    .Select(SortedOrganisation)
                    .ToList()
            }
        };

你的回答很有道理,我正在按照你建议的解决方案进行工作,但很快会回复你。谢谢! - haroonxml

3
您可以在之前进行排序:
    organisations.ToList().ForEach(o => o.Departments = o.Departments.OrderBy(d => d.Code).ToList());
    organisations.SelectMany(o => o.Departments).ToList().ForEach(d => d.Employees = d.Employees.OrderBy(e => e.Name).ToList());

然后使用已排序的列表

        var legalEntitiesCollectionByType = new Dictionary<string, ICollection<Organisation>>
        {
            {
                "Institutional", organisations
                    .Where(x => x.Type == "Institutional")
                    .ToList()
            },
            {
                "Retail", organisations
                    .Where(x => x.Type == "Retail")
                    .ToList()
            }
        };

注意:排序是非就地排序的,您可以使用比较器实现就地排序。
        organisations.ToList().ForEach(o => o.Departments.Sort(CreateCustomComparison));
        organisations.SelectMany(o => o.Departments).ToList().ForEach(d => d.Employees.Sort(CreateCustomComparison));

为什么在Linq中已经实现了所有功能,你还要使用比较器?Linq的OrderBy已经很好地完成了这项工作。 - haroonxml
因为 'orderby' 不会就地排序,它会生成一个新的 IEnumerable,我需要对其进行赋值。相反,'Sort' 方法会就地完成排序工作。 - Toto
这些家伙真是与众不同 https://dev59.com/3XA75IYBdhLWcg3wSm64 - haroonxml

2
如果需要按照员工的代码和姓名排序,你可以将该属性设置为 SortedList<>
public class Department
{
    ...
    public SortedList<Tuple<int, string>, Employee> Employees { get; set; }
}

在 .NET 4 之前,您可以使用 KeyValuePair 代替 Tuple
在创建 Employees 对象时,需要为排序列表的键提供 IComparer 对象。
Employees = new SortedList<Tuple<int, string>, Employee>(new EmployeeKeyComparer());

EmployeeKeyComparer 可以定义为:

public class EmployeeKeyComparer : IComparer<Tuple<int, string>>
{
    public int Compare(Tuple<int, string> x, Tuple<int, string> y)
    {
        if (x.First == y.First)
            return StringComparer.Ordinal.Compare(x.Second, y.Second);
        else
            return x.First.CompareTo(y.First);
    }
}

谢谢您的建议,但如果我正在使用LINQ OrderBy,就不需要实现IComparer。 - haroonxml
@HaroonEmmanuel,如果列表始终需要排序,则最好只对其进行一次排序,而不是每次使用时都使用LINQ对其进行排序。 - Dialecticus

-1
var legalEntitiesCollectionByType = new Dictionary<string, ICollection<Organisation>>
            {
                {
                    "Institutional", organisations
                        .Where(x => x.Type == "Institutional")
                        .ToList()
                        .Select(o => new Organisation{Code = x.Code,Departaments = x.Departaments.OrderBy(c => c).ToList()  }).ToList()
                }
            }

2
请在您的回答中提供更多信息。为什么这段代码比被接受的答案工作得更好/不同? - mhlz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接