Implementing IComparer for Sorting Custom Objects

In a previous post I discussed the usefulness of implementing IComparable for your classes to assist in sorting your custom objects in ArrayLists.  When you call Sort() on the ArrayList, the default implementation of IComparer is called which uses QuickSort.  QuickSort calls the IComparable implementation of CompareTo() on each of your objects in the ArrayList.


Implementing IComparable for Sorting Custom Objects


Sometime this isn’t enough and you will want to pass in your own IComparer for more flexibility, such as to allow you to specify by which field you want to sort the custom objects in the ArrayList.


 



IComparer Defined


Compares two objects and returns a value indicating whether one is less than, equal to or greater than the other.


int Compare(object x, object y);


 


Rather than calling the Sort() method on the ArrayList and using the default IComparer, we are going to use an overloaded method of the ArrayList Sort method which allows us to pass in our own IComparer:


 



Sort(IComparer comparer)


public virtual void Sort(IComparer comparer);


 


In keeping with the same People ArrayList and Person Class we have been using in our examples, here is our custom PersonComparer Class that implements IComparer for use in our custom sorting:


 


PersonComparer
public class PersonComparer : IComparer
{
public enum ComparisonType
{ Firstname
= 1, Lastname = 2, Age = 3 }

private ComparisonType _comparisonType;

public ComparisonType ComparisonMethod
{
get { return _comparisonType; }
set { _comparisonType = value; }
}

#region IComparer Members

public int Compare(object x, object y)
{
Person p1;
Person p2;

if (x is Person)
p1
= x as Person;
else
throw new ArgumentException(Object is not of type Person.);

if (y is Person)
p2
= y as Person;
else
throw new ArgumentException(Object is not of type Person.);

return p1.CompareTo(p2, _comparisonType);
}

#endregion
}


 


The class itself won’t be doing any comparisons.  It only keeps track of which field you want to compare by and delegates the comparison to the Person class itself by calling an overloaded method of CompareTo implemented by Person:


 


Person’s Overloaded CompareTo Method
public int CompareTo(Person p2, PersonComparer.ComparisonType comparisonMethod)
{
switch (comparisonMethod)
{
case PersonComparer.ComparisonType.Lastname :
return _lastname.CompareTo(p2._lastname);
case PersonComparer.ComparisonType.Age :
return _age.CompareTo(p2._age);
case PersonComparer.ComparisonType.Firstname :
default:
return _firstname.CompareTo(p2._firstname);
}
}

 


And, of course, as shown above, the overloaded version of CompareTo just delegates the comparison right back to the String and Int32 classes, which implement IComparable.


Here is the complete code listing that you can run in Snippet Compiler.  It essentially sorts the People ArrayList 3 different times by Lastname, Age, and Firstname, respectively.


 


Testing IComparer
using System;
using System.Collections;

public class Person : IComparable
{
#region Private Members

private string _firstname;
private string _lastname;
private int _age;

#endregion

#region Properties

public string Firstname
{
get { return _firstname; }
set { _firstname = value; }
}

public string Lastname
{
get { return _lastname; }
set { _lastname = value; }
}

public int Age
{
get { return _age; }
set { _age = value; }
}

#endregion

#region Contructors

public Person (string firstname, string lastname, int age)
{
_firstname
= firstname;
_lastname
= lastname;
_age
= age;
}

#endregion

#region ToString()

public override string ToString()
{
return String.Format({0} {1}, Age = {2}, _firstname,
_lastname, _age.ToString());
}

#endregion

#region IComparable Members

public int CompareTo(object obj)
{
if (obj is Person)
{
Person p2
= (Person)obj;

return _firstname.CompareTo(p2._firstname);
}
else
throw new ArgumentException(Object is not a Person.);
}

#endregion

public int CompareTo(Person p2, PersonComparer.ComparisonType comparisonMethod)
{
switch (comparisonMethod)
{
case PersonComparer.ComparisonType.Lastname :
return _lastname.CompareTo(p2._lastname);
case PersonComparer.ComparisonType.Age :
return _age.CompareTo(p2._age);
case PersonComparer.ComparisonType.Firstname :
default:
return _firstname.CompareTo(p2._firstname);
}
}

#region PersonComparer

public class PersonComparer : IComparer
{
public enum ComparisonType
{ Firstname
= 1, Lastname = 2, Age = 3 }

private ComparisonType _comparisonType;

public ComparisonType ComparisonMethod
{
get { return _comparisonType; }
set { _comparisonType = value; }
}

#region IComparer Members

public int Compare(object x, object y)
{
Person p1;
Person p2;

if (x is Person)
p1
= x as Person;
else
throw new ArgumentException(Object is not of type Person.);

if (y is Person)
p2
= y as Person;
else
throw new ArgumentException(Object is not of type Person.);

return p1.CompareTo(p2, _comparisonType);
}

#endregion
}

#endregion
}

public class TestClass
{
public static void Main()
{
// Create ArrayList
ArrayList people = new ArrayList();
people.Add(
new Person(John,Doe, 76));
people.Add(
new Person(Abby,Normal, 25));
people.Add(
new Person(Jane,Doe, 84));

// Create Person Comparer Class
Person.PersonComparer comparer = new Person.PersonComparer();

// Sort By Lastname
comparer.ComparisonMethod = Person.PersonComparer.ComparisonType.Lastname;
people.Sort(comparer);
TestClass.DisplayPeople(people);

// Sort By Age
comparer.ComparisonMethod = Person.PersonComparer.ComparisonType.Age;
people.Sort(comparer);
TestClass.DisplayPeople(people);

// Sort By Firstname
comparer.ComparisonMethod = Person.PersonComparer.ComparisonType.Firstname;
people.Sort(comparer);
TestClass.DisplayPeople(people);
}

public static void DisplayPeople(ArrayList people)
{
foreach (Person p in people)
Console.WriteLine(p);

Console.ReadLine();
}
}


 


Now we have a way to sort our custom objects in multiple ways using the custom PersonComparer class.  Check out all my Back to Basics Articles.

This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

One Response to Implementing IComparer for Sorting Custom Objects

  1. Joel Ross says:

    Thought you might be interested to see this:

    http://www.rosscode.com/blog/index.php?title=generic_multiple_sorting_for_typed_colle&more=1&c=1&tb=1&pb=1

    This is a generic way to sort on any column of any object using reflection.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>