Click here to Skip to main content
15,914,013 members
Articles / Programming Languages / C#

Custom Sort a List of Objects by Any Order

Rate me:
Please Sign up or sign in to vote.
4.93/5 (16 votes)
11 Mar 2012CPOL4 min read 79.4K   91   25  
An IList Extension Method that sorts the list in a very specific order or in a combination of orders.
This is an old version of the currently published article.

Introduction

Recently, I encountered a problem at my work - a list of account objects needed to appear in a specific order in a report, and it had to look like:

  • ISA Stock & Shares
  • ISA Cash
  • Wrap Cash
  • Personal Portfolio

The order is by the Account object's AccountType property. Apparently, I cannot use the IEnumerable.OrderBy() method as the order required is not alphabetical. I could add an extra property - Order - to the Account object, but I don't like the idea of bloating my class. Besides, in a different scenario, the order may differ. I could, of course, make the Account class implement the IComparable interface, but again, the order may be different for each scenario, and I will have to modify the Account class to cope with new sorting scenarios. Besides, sometimes you may not have access to modify an existing class. So a better solution would be to separate the sorting algorithm from the class you want to sort. In this way, it can vary independently (seems a favourite sentence in the GoF Design Pattern book :)

So I ended up developing an IList Extension Method and a Sorter class which mimics the syntax of IEnumerable.OrderBy() and lets you custom sort (rather than alphabetical) a list of objects by its string or enum properties. (The Sorter class doesn't implement the IComparer interface for reasons I will discuss later.)

How to Use CustomSort

To sort a list by a single property:

C#
_listToBeSorted.Sort(x => x.AccountType, 
  "ISA Stock & Shares,ISA Cash,Wrap Cash,Personal Portfolio");

you can specify a StringComparison to control how strings are compared when doing the sort:

C#
_listToBeSorted.Sort(x => x.AccountEnum, 
  "ISA Stock & Shares,ISA Cash,Wrap Cash,Personal Portfolio", 
  StringComparison.InvariantCultureIgnoreCase);

If you want to sort by more than one property, you have to use the Sorter class. I employed the Fluent Interface technique to make the code more readable:

C#
var comparer = new Sorter<ClassToBeSorted, string>(); 

_listToBeSorted.Sort(
  comparer.Add(x => x.AccountType, 
               "ISA Stock & Shares,ISA Cash,Wrap Cash,Personal Portfolio") 
      .Add(x => x.AccountName, "Peter,Adam",
           StringComparison.InvariantCultureIgnoreCase)
      .Add(x => x.AccountLocation, "London,Edinburgh")); 

If your object contains other objects as properties, for example:

C#
public class CompositeClassToBeSorted { 
   public ClassToBeSorted ClassProperty; 
   public string ClassName; 
}

you can drill down as deep as you want to use a certain property for sorting:

C#
_listToBeSorted.Sort(x => x.ClassProperty.AccountType, "wrap,Cash");

The IEnumerable.OrderBy() method doesn't sort correctly if you want to sort by a certain element in a list. For example, if you have a class like this:

C#
public class ListClass
{
    public List<ClassToBeSorted> ClassProperty;
}

the following won't work:

C#
_listToBeSorted.OrderBy(x => x.ClassProperty[3].AccountType);

However, you can use the custom sorter to sort by certain elements in a property of List type:

C#
classList.Sort(x => x.ClassProperty[0].AccountType, "wrap,Cash");

The Sorting Behaviour, Implementation, and Performance

If your whole list contains A, B, C, D, E, F, and you only specify partial order, i.e.:

C#
_listToBeSorted.OrderBy(x => x.AccountType,"B,E"); 

then B, E will come on top of the list, and the rest of the elements will remain where they are.

Initially, I made CustomSort to implement the IComparer interface and use the IList.Sort(IComparer) method to sort the list. It works like a charm if the custom sort order contains every element in the list. But when we only specify a partial order, the remaining elements don't stay where they are, but kind of randomised (or maybe, there is a pattern that I failed to see). So I ended up sorting the list myself.

The performance could be better. I tested it with a list of 1000 objects. If the sort is only by one property, sorting completes in a split second. But if you sort by two or three properties, the sort will take a few seconds. I have included a performance test in the attached source file. You can see for yourself whether the performance is satisfactory for you.

Possible Improvements

  1. At the moment, the custom sort only works for the generic IList. It is easy enough to do the same for IEnumerable. It is just that I found IList to be the most common collection, and you can convert IEnumerable to List easily by calling the ToList() method.
  2. The custom sort can sort by a property which is a List, classList.Sort(x => x.ClassProperty[0].AccountType, "wrap,Cash"), but it doesn't do so for Dictionary type properties. Again, this can be easily achieved.
  3. Performance can be improved, but I haven't been bothered to try out various sorting algorithms. I just did enough to satisfy the requirement of my own work situation.

About the Source Code

The source code is in .NET 3.5. Some of the .NET 4.0 features, like optional parameters, dynamics, etc., will make the code more concise and maybe more efficient, but for better backwards compatibility, I stuck to .NET 3.5.

The tests are written in xunit in a Behaviour Driven Development (BDD) style. When executed in ReSharper, it gives the nice documentation-like specs.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Team Leader XYZ Studios Ltd
China China
I am an independent IT contractor who is passionate about life and Microsoft technology stack. I have been programming since 2001. Besides coding, I also enjoy diving, swimming, rock climbing and travelling. I swim 2 miles a day to keep my morale and energy level consistently high.

Comments and Discussions

Discussions on this specific version of this article. Add your comments on how to improve this article here. These comments will not be visible on the final published version of this article.