Sort a list and all its nested objects using LINQ

18,381

Solution 1

You need to do all three levels of sorting inside the objects that you return, like this (I'll show only the "Retail", the "Institutional" needs to be sorted in the same way):

{
"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()
}

Since you need to select this multiple times, you may want to wrap this code in a method, and use it from several spots, like this:

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

Solution 2

I know this is an old question, but there's a simpler way of achieving the same result:

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

Solution 3

If Employees ever need to be sorted by code and name then you can make that property a SortedList<>.

public class Department
{
    ...
    public SortedList<Tuple<int, string>, Employee> Employees { get; set; }
}

Prior to .NET 4 you could use KeyValuePair instead of Tuple.

When creating Employees object you'd need to provide IComparer object for sorted list's key.

Employees = new SortedList<Tuple<int, string>, Employee>(new EmployeeKeyComparer());

where EmployeeKeyComparer could be defined as

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

Solution 4

You can sort before :

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

And then use the list already sorted

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

NB : the sort is not in place, you can achieve this using a comparer

        organisations.ToList().ForEach(o => o.Departments.Sort(CreateCustomComparison));
        organisations.SelectMany(o => o.Departments).ToList().ForEach(d => d.Employees.Sort(CreateCustomComparison));
Share:
18,381
haroonxml
Author by

haroonxml

Updated on July 17, 2022

Comments

  • haroonxml
    haroonxml almost 2 years

    There is an organisation with several departments and each department has a few employees.

    I have created the following object model:

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

    Now, I have a list of these organisations and using LINQ, I would like to sort/order the output as follows:

    1) Organisations: Ordered by Code and Name

    2) Departments: Ordered by Code and Name

    3) Employees: Ordered by Code and Name

    Below is some test data that I have populated:

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

    Here I am adding the data to a dictionary:

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

    doing it this way the sorting only happens at the Organisation level and not on the department or the employee level.

    My question is, how can I achieve sorting on all 3 levels within the object hierarchy while populating the dictionary above?

    Cheers

  • haroonxml
    haroonxml over 10 years
    Your answer makes sense and I am working on your suggested solution but will get back to you shortly. Cheers
  • haroonxml
    haroonxml over 10 years
    thanks for your suggestion but if i am using linq orderby i shouldnt have the need to implement an IComparer.
  • haroonxml
    haroonxml over 10 years
    why use comparer when you have all that functionality implemented within linq ? . Linq's orderby does the job pretty well.
  • Dialecticus
    Dialecticus over 10 years
    @HaroonEmmanuel, if the list is always required to be sorted then it makes sense to sort it only once, rather than sort it with linq every time it's used.
  • Toto
    Toto over 10 years
    Because 'orderby' does not order 'in place', it generates a new IEnumerable and I need to assign it. 'Sort' method, on the contrary, does the job 'in place'.
  • haroonxml
    haroonxml over 10 years
  • mhlz
    mhlz about 9 years
    Please provide more information in your answer. Why does this code work better/different than the accepted answer?
  • Tom Baxter
    Tom Baxter over 7 years
    This is the best answer, in my opinion. There is a typo -- Departmets should be Departments and each of the "...ThenBy(...) needs to end with a .ToList();
  • Hellraiser
    Hellraiser over 7 years
    Thanks for pointing out the typo. As for the .ToList() I don't think it's really necessary. I'll check and let you know.