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

A Simple and Generic sorting technique for your business object collection

By , 19 Aug 2005
 

Introduction

Stop writing IComparer classes to sort your custom collections! This article discusses sorting of a user defined collection object based on any of the properties of the business entity. This sorting technique is generic for all the collection objects. You can sort your collection based on their properties.

Scenario

For example consider the "Student" and "StudentCollection" classes:

public class Student
 {
     string name="";
     public string Name
     {
         get{return name;}
         set{name = value;}
     }
     int id=0;
     public int Id
     {
         get{return id;}
         set{id = value;}
     }
     DateTime dob=DateTime.MinValue;
     public DateTime DOB
     {
         get{return dob;}
         set{dob = value;}
     }
     public Student(string name,int id,DateTime Dob)
    {
         this.name = name;
         this.id = id;
         this.dob = Dob;
    }
 }
 public class StudentCollection : CollectionBase
 {
     public StudentCollection()
     {
     } 
     //Other code ……..
     // ...
 }

Student collection consists of Student object collection. Student entity consists of three properties namely, name, id and Dob. Suppose you want to sort the collection based on the Student's Name or Id or DOB, we need to write the Comparer class for each user defined collection as follows.

Usual way of sorting objects using IComparer and IComparable interfaces

class StudentComparer : IComparer
{
    private int intCompType;
    public StudentComparer (int sortOrder)
    {
        intCompType = sortOrder;
    }
    public int Compare(object x,object y)
    {
        switch(intCompType)
        {
            case (int)enuSortOrder.NameAsc:
                return ((Student)x).Name.CompareTo(((Student)y).Name);
            case (int)enuSortOrder.NameDesc:
                return ((Student)y).Name.CompareTo(((Student)x).Name);
        }
    }
}

Problem

For StudentCollection we need a StudentComparer class and for ProductCollection we need a ProductComparer class…and so on…. To avoid these cumbersome coding, I wrote a generic "SortableCollectionBase" class that can be used to sort any custom collection object without tedious coding.

Solution

How it works?

SortableCollectionBase class uses "GenericComparer" class for sorting. "GenericComparer" class implements IComparer interface and compares the objects based on the public property (Sort Column) of the class type dynamically irrespective of the collection.

GenericComparer class

/// <summary>
/// This class is used to compare any 
/// type(property) of a class.
/// This class automatically fetches the 
/// type of the property and compares.
/// </summary>
public sealed class GenericComparer:IGenericComparer 
{
    /// <summary>
    /// Sorting order
    /// </summary>
    public enum SortOrder
    {
        Ascending = 0,
        Descending = 1
    }
    Type objectType;
    /// <summary>
    /// Type of the object to be compared.
    /// </summary>
    public Type ObjectType
    {
        get{return objectType;}set{objectType = value;}
    }
    string sortcolumn = "";
    /// <summary>
    /// Column(public property of the class) to be sorted.
    /// </summary>
    public string SortColumn
    {
        get{return sortcolumn;}set{sortcolumn = value;}
    }
    int sortingOrder = 0;
    /// <summary>
    /// Sorting order.
    /// </summary>
    public int SortingOrder
    {
        get{return sortingOrder;}set{sortingOrder = value;}
    }
    /// <summary>
    /// Compare interface implementation
    /// </summary>
    /// <param name="x">Object 1</param>
    /// <param name="y">Object 2</param>
    /// <returns>Result of comparison</returns>
    public int Compare(object x, object y)
    {
        //Dynamically get the protery info 
        //based on the protery name
        PropertyInfo propertyInfo = 
              ObjectType.GetProperty(sortcolumn);
        //Get the value of the instance
        IComparable obj1 = 
              (IComparable)propertyInfo.GetValue(x,null) ;
        IComparable obj2 = 
              (IComparable)propertyInfo.GetValue(y,null) ;
        //Compare based on the sorting order.
        if(sortingOrder == 0)
        return ( obj1.CompareTo(obj2) );
        else
        return ( obj2.CompareTo(obj1) );
    }
}

SortableCollectionBase class

/// <summary>
/// Abstract implementation of Sortable collection.
/// </summary>
public abstract class SortableCollectionBase : 
                            CollectionBase,ISortable
{
    string sortcolumn="";
    public string SortColumn
    {
        get{return sortcolumn;}
        set{sortcolumn = value;}
    }
    GenericComparer.SortOrder sortingOrder = 
              GenericComparer.SortOrder.Ascending;
    public GenericComparer.SortOrder SortingOrder
    {
        get{return sortingOrder;}set{sortingOrder = value;}
    }
    Type sortObjectType;
    public Type SortObjectType
    {
        get{return sortObjectType;} set{sortObjectType = value;} 
    }
    public virtual void Sort() 
    {
        if(sortcolumn == "") 
            throw new Exception("Sort column required."); 
        if(SortObjectType == null) 
            throw new Exception("Sort object type required."); 
        IGenericComparer sorter = new GenericComparer();
        sorter.ObjectType = sortObjectType;
        sorter.SortColumn = sortcolumn;
        sorter.SortingOrder = (int)sortingOrder;
        InnerList.Sort(sorter);
    }
}

How to use SortableCollectionBase class?

Using SortableCollectionBase is simple and effortless. Just inherit your custom collection class from SortableCollectionBase class and in the constructor set the SortableObjectType property. Now your class becomes sortable.

For example

/// <summary>
/// Note : This student collection 
/// inherhits SortableCollectionBase
/// In the constructor set the 
/// SortObjectType for sorting.
/// </summary>
public class StudentCollection : 
                      SortableCollectionBase
{
    public StudentCollection()
    {
        //In your collection class 
        //constructor add this line.
        //set the SortObjectType for sorting.
        base.SortObjectType = typeof(Student);
    }
    public Student this[ int index ] 
    {
        get 
        {
            return( (Student) List[index] );
        }
        set 
        {
            List[index] = value;
        }
    }
    public int Add( Student value ) 
    {
        return( List.Add( value ) );
    }
    public int IndexOf( Student value ) 
    {
        return( List.IndexOf( value ) );
    }
    public void Insert( int index, Student value ) 
    {
        List.Insert( index, value );
    }
    //......
}

How to sort?

To sort your custom collection call the "Sort()" method. Note: Make sure that you have set the "SortColumn" property before calling the "Sort()" method. "SortColumn" is the property of the business entity based on which the collection will be sorted. In this case SortColumn can be "name", "id" or "Dob".

StudentCollection Students = new StudentCollection();
Students.Add(new Student("Sai",5,new DateTime(1914,10,4)));
Students.Add(new Student("Sree",1,new DateTime(1980,10,4)));
Students.Add(new Student("Sow",3,new DateTime(2000,4,1)));
Students.Add(new Student("Zaheer",2,new DateTime(1978,1,27)));
Students.SortColumn = "Name";
Students.SortingOrder = 
     GenericComparer.SortOrder.Ascending;
Students.Sort();
dataGrid1.DataSource = Students;

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

SreeKrishna
Web Developer
India India
Member
No Biography provided

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   
QuestionGenerics?membertmag25 Feb '08 - 6:34 
Hey,
 
I was just wondering if anyone has tried to update this using Generics?
 
Regards,
 
Todd
AnswerRe: Generics?memberrazaross21 Dec '09 - 13:23 
This is what you're looking for I believe: http://www.c-sharpcorner.com/UploadFile/dipenlama22/SortingUsingGeneric07052006081035AM/SortingUsingGeneric.aspx[^]
QuestionThe data doesn't show upmemberhschan824 Sep '07 - 12:12 
I am trying to implement this using 2005. All I see are blank cells, but the number of rows match what is in the collection. What could be wrong? I set the datapropertyname in each of my datagridview columns making sure they match the property names exactly in my business object. Then I just do dgv.datasource = myObjCollection
 
Any pointers appreciated. Thank you.
Howard
GeneralVery good - Simple and useful...memberNarasinhaSource1 Aug '07 - 12:38 
Liked a lot..Was looking for this..
GeneralWorks great in VS2003. Maybe an update for VS2005memberhfourie29 Apr '07 - 0:03 
Great Article. I have implemented this in a few collections and it works very well. I need to upgrade the system to .Net 2.0, but I cannot seem to get the classes to compile. Keep on getting Error
"Using the generic type 'System.Collections.Generic.IComparer' requires '1' type arguments"
 
Any ideas?Confused | :confused:
GeneralGreat PostmemberSamStange13 Feb '07 - 7:22 
Great Post! I was looking for an equivalent to Jonathan Cogley's Thycotic comparer for 2.0, and found it! Great job, code works great.
 
Sam
GeneralWorks great!memberKoen Zomers21 Aug '06 - 6:19 
I've made a slight modification so it works with generic methods in .NET 2.0. Works great now! I don't have to write a collection class for each business object anymore. Just one does the trick. Thanks for this great piece of code.
General:)memberDaniel@SA9 Apr '06 - 23:16 
Defiantly worth at least having a look!
 
I have used something similar in the past, but since seeing this, iv adopted this into any project when has the need or use for a storable collection. At the very least it offers teachings in inheritance and abstraction... well needed technologies in this day and age. Two thumbs up and 5 out of 5 from me!

QuestionSorting on multiple properties at once?memberversat14747 Sep '05 - 6:29 
Looks good for basic sorting. However, have you thought about how to sort on multiple properties at a time? Maybe it is currently sorted on "Name" ascending, and just in case the name is the same for multiple entries, you want a secondary sort on "DOB". In the local phone directory just in my city alone there is more than one person with my exact same name. A secondary sort would be very helpful in this kind of situation.
 
I typically don't create a separate IComparer class either. Instead i create an enum inside the class holding values for each property/direction that can be sorted. Then have methods AddSort(SortOption option), ClearSort(), Sort(), and ReverseSort(). The collection class itself implements IComparer. The Compare method looks at the sortoptions field defined in the current collection instance to do the comparison. Since each collection instance exposes how it is sorted, the ui can add sort direction icons by looking at the SortOptions property. Maybe I should write an article on my sort method?
AnswerRe: Sorting on multiple properties at once?memberDaniel Brown (SmartSoft)22 Feb '06 - 16:22 
Great Sortable Collection. However I do agree that it would be a huge extra bonus if the class allowed sorting on multiple fields as the previous poster stated.
 
If anyone has any suggestions on how to adapt the sortable collections to have sorting on multiple fields, it would be a great help Smile | :) as for now, ill plug around with it and see what I can get working Smile | :)
QuestionHow to Check Performancememberanig123425 Aug '05 - 0:43 
Your article is very useful. How can we check the Performance . i mead Which tool we used for it
 
Thanks
 


GeneralPerformancememberSean Winstead20 Aug '05 - 13:21 
Did you do any performance measurements comparing your GenericComparer with a type-specific comparer? I'm wondering how much slower GenericComparer happens to be.
 
--
Sean Winstead

GeneralRe: PerformancememberSreeKrishna20 Aug '05 - 18:51 
Hi,
Thanks for your suggestion. I did performance testing on GenericComparer.Generic comparer performs good with records less than 1 million.And
Ofcourse Generic Comparer will be slower when the number of records is more than a million. Its a know issue. I suggest to use Type-specific comparer if there more than 1 million records to be sorted.
 
A small change in Generic comparer class will improve performnace
 
public sealed class GenericComparer:IGenericComparer
{
 
PropertyInfo propertyInfo;
 
///
/// Sorting order
///

public enum SortOrder
{
Ascending = 0,
Descending = 1
}
 
Type objectType;
///
/// Type of the object to be compared.
///

public Type ObjectType
{
get{return objectType;}set{objectType = value;}
}
 

string sortcolumn = "";
///
/// Column(public property of the class) to be sorted.
///

public string SortColumn
{
get{return sortcolumn;}set{sortcolumn = value;}
}
 
public GenericComparer(Type ObjectType,string sortcolumn)
{
this.objectType = objectType;
//Dynamically get the protery info based on the protery name
propertyInfo = ObjectType.GetProperty(sortcolumn);
}
 
int sortingOrder = 0;
///
/// Sorting order.
///

public int SortingOrder
{
get{return sortingOrder;}set{sortingOrder = value;}
}
 
///
/// Compare interface implementation
///

/// Object 1
/// Object 2
/// Result of comparison
public int Compare(object x, object y)
{

//Get the value of the instance
IComparable obj1 = (IComparable)propertyInfo.GetValue(x,null) ;
IComparable obj2 = (IComparable)propertyInfo.GetValue(y,null) ;
//Compare based on the sorting order.
if(sortingOrder == 0)
return ( obj1.CompareTo(obj2) );
else
return ( obj2.CompareTo(obj1) );
}
}

GeneralRe: Performancemembercaractacus23 Aug '05 - 22:07 
Your point is well made.
 
However, I would not recommend loading 1+ million instances into a collection.
 
In fact, I would not recommend loading even 10% of that.
 
With Generics in .NET v2.0, it is a trivial matter to type the IComparer implementation, and this would of course be the preferred solution, incorporating strong typing.
 
To the author, thanks for a simple, effective pattern. I was expecting use of reflection, or some other convoluted approach. Well done!
 

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.130516.1 | Last Updated 20 Aug 2005
Article Copyright 2005 by SreeKrishna
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid