Click here to Skip to main content
Click here to Skip to main content

How To Sort Generic List?

By , 12 Aug 2009
 

Introduction

Some time ago, I was asked a question related to generic List<T>. How to sort any generic list based on some data member of any user defined class. Let me admit that I have been using C#.NET since the last 3 years but at that moment nothing comes into my mind directly. There is a method named Sort() - that's what the first thought was. So thereafter I tried it by making a sample application. There are various ways to achieve it but with this small article I am posting my solution which I think is very simple.

Background 

First let's understand the problem with one simple example. Let's say we are having a list of employees for some organization. Now to generate a report, there are few requirements to sort that list name-wise or employeeid-wise, etc. So our problem is how to achieve it via generic list so that very limited changes allow us to sort it on any of the attributes.

Using the Code

Let's have a user defined Employee class.

public class Employee
    {
        #region Private Members

        private Int32 empId;
        private String empName;
        private DateTime empJoiningDate;

        #endregion

        #region Properties

        public Int32 EmployeeId
        {
            get { return empId; }
            set { empId = value; }
        }

        public String EmployeeName
        {
            get { return empName; }
            set { empName = value; }
        }

        public DateTime EmployeeJoiningDate
        {
            get { return empJoiningDate; }
            set { empJoiningDate = value; }
        }

        #endregion
    }

Now whenever we want to use the sorted Employee list, first of all we will create the List<T>.

List<Employee> myEmployeeList = new List<Employee>();

The next step would be using the Sort() method & inside the method writing another easy-to-find method CompareTo(). But the problem with this approach is that sometimes we have to specify that sorting has to be based on some specific attributes only (i.e. ID or Name of the employees).

public int CompareTo(Employee other)
{
    return EmployeeName.CompareTo(other.EmployeeName);
}

The above method can be useful only when we are sure about the attribute for which sorting is to be done. Otherwise it's better to have the parameter to be passed for sorting. To achieve this, we should use IComparable<T>. (Click here for details.)

public class Employee : IComparable<Employee>
{
    [...]
       
    public Int32 CompareTo(Employee other)
    {
        return EmployeeName.CompareTo(other.EmployeeName);
    }            
       
    [...]
}

Here we are having fix attribute Name for sorting. To add flexibility, we can use delegates to avoid the code repetition. We can declare multiple delegates for each attribute upon which sorting is allowed.

public class Employee : IComparable<Employee>
{
    [...]
       
    /// <summary>
    /// Comparison of age between the employees.
    /// </summary>
    public static Comparison<Employee> AgeComparison = 
				delegate(Employee p1, Employee p2)
    {
        return p1.empAge.CompareTo(p2.empAge);
    };
        
    /// <summary>
    /// Comparison of name between the employees.
    /// </summary>
    public static Comparison<Employee> NameComparison = 
				delegate(Employee p1, Employee p2)
    {
        return p1.empName.CompareTo(p2.empName);
    };           
       
    [...]
}

By declaring static delegates, we can use them without creating the instance of Employee class while passing as parameter to Sort() method.

myEmployeeList.Sort(Employee.NameComparison);
[...]
myEmployeeList.Sort(Employee.AgeComparison);

Let's have a look at the full code of Employee class:

/// <summary>
/// This class represents the structure, group of this structure is to be sorted.
/// </summary>
public class Employee : IComparable<Employee>
{
    #region Private Members

    private Int32 empId;
    private String empName;
    private UInt32 empAge;

    #endregion
  
    #region Properties
        
    /// <summary>
    /// Gets or sets the employee id / no
    /// </summary>
    public Int32 EmployeeId
    {
        get { return empId; }
        set { empId = value; }
    }

    /// <summary>
    /// Gets or sets the name of the employee
    /// </summary>
    public String EmployeeName
    {
        get { return empName; }
        set { empName = value; }
    }

    /// <summary>
    /// Gets or sets the Age of the employee
    /// </summary>
    public UInt32 EmployeeAge
    {
       get { return empAge; }
       set { empAge = value; }
    }

    #endregion

    #region Constructor
          
    /// <summary>
    /// Creates an instance of an employee class.
    /// </summary>
    /// <param name="id"> Id / no of the employee to be created.</param>
    /// <param name="name">Name of the employee to be created.</param>
    /// <param name="age">Age of the employee to be created.</param>
    public Employee(Int32 id, String name, UInt32 age)
    {
        this.empId = id;
        this.empName = name;
        this.empAge = age;
    }
        
    #endregion

    #region Static Members
    /// <summary>
    /// Comparison of age between the employees.
    /// </summary>
    public static Comparison<Employee> AgeComparison = delegate(Employee p1, Employee p2)
    {
        return p1.empAge.CompareTo(p2.empAge);
    };

    /// <summary>
    /// Comparison of name between the employees.
    /// </summary>
    public static Comparison<Employee> NameComparison = delegate(Employee p1, Employee p2)
    {
        return p1.empName.CompareTo(p2.empName);
    };

    /// <summary>
    /// Comparison of employee Id / No between the employees.
    /// </summary>
    public static Comparison<Employee> IDComparison = delegate(Employee p1, Employee p2)
    {
        return p1.empId.CompareTo(p2.empId);
    };

    #endregion

    #region IComparable<Product> Members
          
    /// <summary>
    /// Compares the current employee object with another object of the same type.
    /// </summary>
    /// <param name="other">An employee object to compare with this object</param>
    /// <returns>A 32-bit signed integer that indicates 
    /// the relative order of the objects being compared.</returns>
    public Int32 CompareTo(Employee other)
    {
        return EmployeeName.CompareTo(other.EmployeeName);
    }

    #endregion

    #region Overridden Methods
           
    /// <summary>
    /// Gets a canonical string representation for the specified employee instance.
    /// </summary>
    /// <returns>A System.String instance that contains the unescaped 
    /// canonical representation of the employee instance</returns>
    public override string ToString()
    {
        return string.Format("Id: {0} Name: {1} Age: {2}", empId, empName, empAge);
    }

    #endregion
}

History

  • 12th August, 2009 -- Article submitted

License

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

About the Author

Anand Malli
Software Developer (Senior)
India India
Member
Anand has been designing and developing applications using C# and the .NET framework since last 3 years.
 
When he's not writing code, you can catch him taking snaps or lost in playing computer games.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5membernguyenminhtien14119 Dec '11 - 19:09 
my english is not good, but thanks, that's so helpful !
GeneralAnother good article on the subjectmemberRTS@Orix9 Sep '09 - 3:43 
If you're interested in sorting lists check out this article written about a year ago on the subject. I have been using the library (updated version from codeplex) for about 9 months now and it works well. Implementation is very easy. Include library and execute the following:
 
List.Sort("column dir, column dir,...");
 
Looks like it is coded for .NET 3.X and will not apply to older versions.
 
http://www.codeproject.com/KB/linq/dynamite_dynamic_sorting.aspx
GeneralRe: Another good article on the subjectmembersognant17 Sep '09 - 23:49 
You have inserted the same link of the article!
repost
thanks!
GeneralI think this is a very good suggestionmemberdbrenth19 Aug '09 - 3:38 
This helped me a bunch. I was looking at having to deal with 8 IComparable Classes and calling each one from another one. Not everyone's sorting algorithm is a simple one line string compare like the other posters seem to claim. I have to go through an 8 or 9 step process to break the "ties". The code just looks much more readable (not to mention reusable) doing it the way you show here.
 
Thanks
 
[Edit:] Your employee class does NOT have to inherit from IComparable<> to use your methods. (You probably know that, but other readers may not notice that you removed the need for this inheritance and your article doesn't state that.)
GeneralRe: I think this is a very good suggestionmemberAnand Malli19 Aug '09 - 20:35 
Nice to hear that this article is helpful to you.
 
Thanks a lot for pointing out the mistake about inheritance from IComparable<>. I should mention it in the article itself.
 
I'm going to update this article by using the features of 3.x Framework as per suggested in other comments. I hope you'll like that part too.
GeneralIt's actually not necessary to create and maintain zillions of static helper methods.memberMichael Epner18 Aug '09 - 4:03 
It's actually not necessary to create and maintain zillions of static helper methods.
 

.NET 2.0:
 
 
myEmployeesList.Sort(delegate(Employee x, Employee y) { return x.No.CompareTo(y.No); });
 
myEmployeesList.Sort(delegate(Employee x, Employee y) { return StringComparer.CurrentCultureIgnoreCase.Compare(x.Name, y.Name); });
 
 

 
.NET 3.x:
 
 
myEmployeesList.Sort((x, y) => x.No.CompareTo(y.No); });
 
myEmployeesList.Sort((x, y) => StringComparer.CurrentCultureIgnoreCase.Compare(x.Name, y.Name); });
 

GeneralRe: It's actually not necessary to create and maintain zillions of static helper methods.memberRich Visotcky18 Aug '09 - 4:54 
If you're using .NET 3.x, it's even simpler. Say you want to order by last name, then first name:
 
myEmployeeList.OrderBy(emp => emp.LastName).ThenBy(emp => emp.FirstName);
 
If you wanted it in descending order by their hire date:
 
myEmployeeList.OrderByDescending(emp => emp.HireDate);
 
However, custom comparers are very important when you want to sort by non-standard types or if you need to sort in a non-standard way, such as sorting dates by year, then month, then day.
 
Also, if you have need to sort an object using the string representation of a property (for instance, data binding to a grid), check out my post at: http://ubernostrum.wordpress.com/2008/08/01/updated-multi-property-sorting-with-linq/[^]
GeneralRe: It's actually not necessary to create and maintain zillions of static helper methods.memberAsher Barak17 Sep '09 - 3:59 
Is it not fasetr using the List.Sort ?
I imagine the OrderBy uses the IEnumerabe and has no internal knowladge of the IList whereas the List itself has knowladge of it's internals and may be able to do a better job.
GeneralRe: It's actually not necessary to create and maintain zillions of static helper methods.memberRich Visotcky21 Sep '09 - 5:20 
That's very possible, but it really depends on what your comparison code looks like, how many objects are in your list, and what kind of objects you're binding to. If you're doing a simple comparison on a numeric or string value, then Sort() would definitely be the way to go. However, if you need to sort a list of custom objects, then you can either go through the troubles of writing a custom comparer or you can use the OrderBy and OrderByDescending extension methods. The extension methods offer some really nice advantages, such as being able to be sorted in a certain way, then sorted further later through the IEnumerable or IQueryable reference. They can also be used on a much larger subset of collections, including custom lists that implement IEnumerable, like the CSLA objects for example.
 
However, I do agree that if performance is your utmost goal and you don't expect many changes to the architecture or usage of your objects, or your objects are simple, then the List.Sort method would be a good way to go. It's still a viable option for custom objects, but if your custom comparer isn't efficient then the List.Sort method will suffer in performance.
GeneralRe: It's actually not necessary to create and maintain zillions of static helper methods.memberAsher Barak23 Sep '09 - 21:24 
I agree.
 
Not however that with lambdas List.Sort() could be a easy as:
 
myList.Sort(el1,el2=>el1.Prop1.CompareTo(el2.Prop1));
 
This will also eliminate the need for reconverting IEmunerable that results from the List.OrderBy() to a new List using ToList (assumnig you need a list). I don't know if the List.OrderBy().ToList() runs the enumerable twice...
 
Anyways this is really something for peformance tewaking...
 
Smile | :)

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 12 Aug 2009
Article Copyright 2009 by Anand Malli
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid