Go to home page mail me! RSS Feed

Sorting custom objects by implementing IComparer

Wednesday, November 12, 2008 2:35 PM

Lets say you have a collections class that implements the System.Collections.CollectionBase that handles a collection of your custom objects and allows you to enumerate and do other cool stuff but no sorting. I was recently faced with this scenario, I was faced with a collection that needs to be sorted by any property/field, I.e. lets say I have a product class, I want to be able to sort it by name, price, etc. just like you would in a database using Order By.

First of, lets talk about IComparer which is very similar to IComparable but used for different purposes so don't get it confused. Also remember that these sorting procedure is for custom objects, you don't have to do this for string or integers since they already support IComparer.

The IComparable allows you to compare two objects as it states but I won't go too deep into that, so lets focus on IComparer, IComparer simply provides additional comparisons features that helps us sort on fields, properties, etc. This interface requires a tertiary comparison, which means that you get a 1,0, or -1 depending on the results of the compare if its greater, equal or less respectively.

 

Here is my custom object compare class

/// <summary>
/// RWilliams - 11/11/2008
/// Generic comparer class that allows use to sort 
/// on a generic collection property/property
/// </summary>
/// <typeparam name="T">the generic type</typeparam>
public class ObjectComparer : IComparer
{
    // Our private variables
    private string propertyName = string.Empty;
    private SortDirection sortDirection = SortDirection.Ascending;
 
    /// <summary>
    /// which direction should the sorting down asc or desc?
    /// </summary>
    public enum SortDirection: int
    {
        Ascending = 0,
        Descending = 1
    }
 
    /// <summary>
    /// the constructor collects the neccessart params, we might want
    /// to add some properties too, if there is a need to.
    /// </summary>
    /// <param name="property"></param>
    /// <param name="sortDirection"></param>
    public ObjectComparer(string propertyName, SortDirection sortDirection)
    {
        this.propertyName = propertyName;
        this.sortDirection = sortDirection;
    }
 
    #region IComparer Members
    /// <summary>
    /// Now lets do some sorting
    /// </summary>
    /// <param name="x">an object x of generic type</param>
    /// <param name="y">an object y of generic type</param>
    /// <returns>an int value that represents the results of the comparison</returns>
    public int Compare(object x, object y)
    {
        // See the property could be anything, Lets use reflection and see 
        // if we can grab  the property/property that needs to be sorted.
        // There are multiple ways to go about doing this.
        // - you could use
        //  1. x.GetType().GetProperty(property).GetValue(x, null);
        //  2. typeof(T).GetProperty(property).GetValue(x, null); if you have passed 
        //      in a generic object "ObjectComparer<T>"
        //  3. System.Reflection.PropertyInfo pi = typeof(T).GetProperty(property)
        //NOTE: I assumed here that x & y are the same, 
        //     if they arn't then split this code
 
        // Patch - this is to support people that actually type ASC or DESC 
        // in their property names--arrggghhhhh
        if (propertyName.ToUpper().EndsWith(" ASC"))
        {
            sortDirection = SortDirection.Ascending;
            propertyName = propertyName.Replace(" ASC", string.Empty);
        }
 
        if (propertyName.ToUpper().EndsWith(" DESC"))
        {
            sortDirection = SortDirection.Descending;
            propertyName = propertyName.Replace(" DESC", string.Empty);
        }
       
        //
        System.Reflection.PropertyInfo pi = x.GetType().GetProperty(propertyName);
        IComparable x1 = pi.GetValue(x, null) as IComparable;
        IComparable y1 = pi.GetValue(y, null) as IComparable;
 
        // we shall return and flip sort direction
        return (sortDirection == SortDirection.Ascending) 
            ? x1.CompareTo(y1) : y1.CompareTo(x1);
    }
 
    #endregion
}

and here is a sample collection base that implements a sort method.

 

/// <summary>
/// RWilliams
/// A custom collection base with sorting and more...
/// </summary>
/// <remarks>I had to create a wholew collection base base the northridge 
/// collection base is really really really slow
/// by doing so and also implementing some functions such as sorting, 
/// I sped the collection object by 80%.
/// </remarks>
/// <typeparam name="T">the generic type that we are sort on</typeparam>
[Serializable()]
public class CollectionBase: System.Collections.CollectionBase
{
    #region [ Sorting ]
    /// <summary>
    /// Perform sort on this object item
    /// </summary>
    /// <param name="field">the property name/field to srt on</param>
    /// <param name="sortDirection">the direction of the sort</param>
    public void Sort(string field, ObjectComparer.SortDirection sortDirection)
    {
        InnerList.Sort(
            new ObjectComparer(field, sortDirection));
    }
 
    /// <summary>
    /// Perform sort on this object item in ascending order
    /// </summary>
    /// <param name="field">the field or propertyname to sort on</param>
    public void Sort(string field)
    {
        InnerList.Sort(new ObjectComparer(
            field, ObjectComparer.SortDirection.Ascending));
    } 
    #endregion
}
 

Now you have all that you need, so if you have a collection of products called "Products" that implements the "CollectionBase" above, this is how you will use it.

Products products = productManager.GetProducts();
products.Sort("ProductName");

the above code will sort the "products" collection by name in ascending order, you can use the overload if you need descending sort.

Your Comments.

  • # re: Sorting custom objects by implementing IComparer

    GravatarThis code is exactly what i've been searching for to accomplish what i've been trying for days. Thanks so much for posting this.

    Left by Scott L. at 1/27/2010 1:51 PM

Your Reply.

Comment Form.

Fields denoted with a "*" are required.

You may also like to leave your email or website.

 
Please add 6 and 6 and type the answer here:

Preview Your Comment.

 
Next entries »