How to sort a generic List<T>

After reading this post from Steven Smith I thought I should write something about it.

Sorting a generic List<T> is pretty straightforward if you know how to do it. With C# 2.0, anonymous methods come at hand, as well as the little known Comparison<T> delegate (check out this post for more information about this class as well as other useful classes new to C# 2.0).

Ok, let's suppose we have a product class (let me save some space by using C# 3.0 syntax).

class Product
{
public int ProductID { get; set; }
public string ProductName { get; set; }
public decimal UnitPrice { get; set; }
}

When we have a list of products we may want to sort it on the ProductName property before displaying it to the user. This can be accomplished with the Sort method of the List<T> class, which defines several overloads. The most handy in this case is the Sort(Comparison<Product>) method and the result is easily achieved with a couple lines of code.

List<Product> products = new List<Product>();

products.Sort(delegate(Product p1, Product p2)
{
return p1.ProductName.CompareTo(p2.ProductName);
});

So far so good, but what if we need to sort our list in several places during the execution of our program? Do we have to write that code each time? Actually no, since we can use the parameterless Sort() method of our list class. What this method does is use the "default comparer" to sort the list. So what's this default comparer? It's the comparer that's automatically created if we implement the IComparable<T> interface. This way we can centralize the sorting logic into our class, and just call the parameterless Sort() method on it whenever we need it sorted on the ProductName property.

public class Product : IComparable<Product>
{
[...]

public int CompareTo(Product other)
{
return ProductName.CompareTo(other.ProductName);
}
}

Ok, now what if we want to be able to sort it on the other two properties, ProductID and UnitPrice? Do we have to write an anonymous method each time as we did in the beginning? Of course no, since there's a useful trick which prevents us from needing to do that. We can define two static Comparer<Product> properties in our product class, and supply them as parameters to the Sort(Comparer<T>) method of our list whenever we need it sorted on something which is not the default sorting logic.

public class Product : IComparable<Product>
{
[...]

public static Comparison<Product> PriceComparison =
delegate(Product p1, Product p2)
{
return p1.Price.CompareTo(p2.Price);
};

public static Comparison<Product> IDComparison =
delegate(Product p1, Product p2)
{
return p1.ProductID.CompareTo(p2.ProductID);
};

[...]
}

Since they are static they can be used simply like so: products.Sort(Product.PriceComparison) or products.Sort(Product.IDComparison), which will respectively sort the list by price and id.

Below is the full code of the Product class.

public class Product : IComparable<Product>
{
private int id;
private string prodName;
private decimal price;

public static Comparison<Product> PriceComparison = delegate(Product p1, Product p2)
{
return p1.price.CompareTo(p2.price);
};

public static Comparison<Product> IDComparison = delegate(Product p1, Product p2)
{
return p1.id.CompareTo(p2.id);
};

public int ProductID
{
get { return id; }
set { id = value; }
}

public string ProductName
{
get { return prodName; }
set { prodName = value; }
}

public decimal UnitPrice
{
get { return price; }
set { price = value; }
}

public Product(int id, string prodName, decimal price)
{
this.id = id;
this.prodName = prodName;
this.price = price;
}

#region IComparable<Product> Members

public int CompareTo(Product other)
{
return ProductName.CompareTo(other.ProductName);
}

#endregion

public override string ToString()
{
return string.Format("Id: {0} Name: {1} Price: {2}", id, prodName, price);
}
}

kick it on DotNetKicks.com

Published 20 June 2007 04:19 AM by simoneb
Filed under:

Comments

# DotNetKicks.com said on 19 June, 2007 08:15 PM

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# Stop_looking_at_me said on 26 June, 2007 04:49 PM
Great article. It's exactly what I was looking for. There's not allot on the web having to do with Comparison, so you're filling the gap.
# simoneb said on 26 June, 2007 07:10 PM

Glad you found it useful!

# chrixian said on 30 June, 2007 01:36 PM
FYI If T doesn't implement IComparable it will automatically try to use the non-generic IComparable interface and if that isn't implmented an exception is thrown
# Will Asrari said on 03 July, 2007 09:06 PM
Very useful. Thanks!
# chrisT said on 03 July, 2007 09:37 PM
Great article, but is it possible to do this in vb.net?
# simoneb said on 04 July, 2007 08:08 AM

Here it is:

Public Class Product
Implements IComparable(Of Product)
' Methods Shared Sub New()
Product.PriceComparison = Function (ByVal p1 As Product, ByVal p2 As Product)
Return p1.price.CompareTo(p2.price)
End Function Product.IDComparison = Function (ByVal p1 As Product, ByVal p2 As Product)
Return p1.id.CompareTo(p2.id)
End Function End Sub Public Sub New(ByVal id As Integer, ByVal prodName As String, ByVal price As Decimal)
Me.id = id
Me.prodName = prodName
Me.price = price
End Sub Public Function CompareTo(ByVal other As Product) As Integer Return Me.ProductName.CompareTo(other.ProductName)
End Function Private Shared Sub Main()
End Sub Public Overrides Function ToString() As String Return String.Format("Id: {0} Name: {1} Price: {2}", Me.id, Me.prodName, Me.price)
End Function ' Properties Public Property ProductID As Integer Get Return Me.id
End Get Set(ByVal value As Integer)
Me.id = value
End Set End Property Public Property ProductName As String Get Return Me.prodName
End Get Set(ByVal value As String)
Me.prodName = value
End Set End Property Public Property UnitPrice As Decimal Get Return Me.price
End Get Set(ByVal value As Decimal)
Me.price = value
End Set End Property ' Fields Private id As Integer Public Shared IDComparison As Comparison(Of Product)
Private price As Decimal Public Shared PriceComparison As Comparison(Of Product)
Private prodName As String End Class
# Peter said on 10 July, 2007 03:16 PM
Simon - an additional thanks for the clear example of sorting with generics.
# simoneb said on 10 July, 2007 03:31 PM

Thanks Peter, glad I helped.

# Satheesh babu said on 12 July, 2007 11:14 AM
Too Good article!!!!
# Tim Haines said on 18 July, 2007 04:28 AM
I'm sure you saved me atleast an hour tonight with this article.
# Philip said on 18 July, 2007 12:24 PM
Hi Simoneb, When I used the code, it wouldnt let me do what you have done with the New() sub. the IDE keeps removing the End Function. Any idea why that might be? I am lost, need a straw
# simoneb said on 19 July, 2007 07:59 PM

I'm sorry Philip, I am not good at coding in vb and did the conversion using Reflector. You should try another conversion tool if it isn't working, you can find many online.

# jbluedelta said on 16 August, 2007 08:15 AM
How would one specify ascending or descending sorts?
# Magnus said on 16 August, 2007 04:39 PM
Great article! Exactly what I've been looking for.
# simoneb said on 19 August, 2007 07:31 AM

@jbluedelta: just invert the order of the comparison.

# waldo said on 22 August, 2007 11:26 AM
Thanks fo the great article. In VB.Net to keep in line with the use of delagates, use "AddressOf" statement instead of adding a constructor to assign functions.
Public shared PriceComparison as Comparison(of Product) = AddressOf PriceComparisonSort 
Public shared IDComparison as Comparison(of Product) = AddressOf IDComparisonSort
Private shared function PriceComparisonSort(p1 as Product, p2 as Product)as integer
return p1.price.CompareTo(p2.price)
end function
private shared function IDComparisonSort(p1 as Product, p2 as Product) as integer
return p1.id.compareTo(p2.id)
end function
# Venkat said on 26 August, 2007 03:16 AM
Simone, Great Thanks for the article. This is straight to the point and useful
# iFX said on 27 August, 2007 04:16 AM

Collections Best Practices

# espinete said on 24 September, 2007 06:33 AM

Hi mister, I've tried sort my List<Entity> but I get error: Failed to compare two elements in the array.

I use this code:

List<TareaWFAsignada> datos = Session[SESSION_SOURCE] as List<TareaWFAsignada>;

               datos.Sort(

                   delegate(TareaWFAsignada t1, TareaWFAsignada t2)

                       return t1.Nexpedval.CompareTo(t2.Nexpedval);

                   });

Nexpedval is string.

And another question, What happened if I have nullable property like "int?" ??

Thanks in advance

anonimoatacante@yahoo.es

# simoneb said on 24 September, 2007 08:22 AM

You should check the kind of exception you get. If you have a nullable type you need to implement a custom compare method.

# DBeahm said on 22 October, 2007 09:49 AM

Saved my life.  This eliminated code that was growing longer and uglier by the second with less than 10 lines of "That was easy".

Great job.

# simoneb said on 22 October, 2007 10:28 AM

Glad you found it useful DBeahm, with lambda expressions this is going to be even more straightforward.

# GeekThings.org said on 01 February, 2008 09:00 AM

Your story was featured in GeekThings.org! Here is the link to vote it up and promote it: www.geekthings.org/.../How_to_sort_a_generic_List_T

# Populate CheckedListBox with a SDE Domain and sort it using LIST « GIS Code Snippet said on 22 April, 2008 12:33 PM

Pingback from  Populate CheckedListBox with a SDE Domain and sort it using LIST &laquo; GIS Code Snippet

# Nick Kirkes said on 09 July, 2008 05:22 PM

Sort a generic List

# John Stagich's Blog said on 26 December, 2008 02:14 PM

December 2008 Quick Hits

# Membership.GetAllUsers(); in una lista | hilpers said on 17 January, 2009 03:57 PM

Pingback from  Membership.GetAllUsers(); in una lista | hilpers

# Sorting Generic List « Private: MY Note said on 16 September, 2009 06:17 AM

Pingback from  Sorting Generic List « Private: MY Note

# Sorting Generic List | Private: My Note said on 25 October, 2009 02:06 PM

Pingback from  Sorting Generic List | Private: My Note

# Simone heneric | Chemicalsalest said on 07 March, 2013 07:28 PM

Pingback from  Simone heneric | Chemicalsalest

# Why does OCaml have two sorting functions: List.sort and List.stable_sort? | Mobiq said on 12 July, 2013 07:38 AM

Pingback from  Why does OCaml have two sorting functions: List.sort and List.stable_sort? | Mobiq

# Define a Generic List property in a static class | user98 said on 11 September, 2013 07:09 PM

Pingback from  Define a Generic List property in a static class | user98

# How do I sort a generic list based on a custom attribute? | Search RounD said on 06 March, 2014 08:12 AM

Pingback from  How do I sort a generic list based on a custom attribute? | Search RounD

This site

Search

Go

This Blog

News

Syndication

Sponsors

  • MaximumASP