Wednesday, 11 March 2009

Sorting an Array of Custom Objects in C#


This post discusses two methods of sorting custom objects within C#.
---------------------------------------------------------------------
Consider a simple case of sorting an array of strings in C#. We can simply call the Sort() method to sort the array.
Code Snippet
  1. ArrayList carArray = new ArrayList();
  2. carArray.Add("Corvette");
  3. carArray.Add("Honda");
  4. carArray.Add("BMW");
  5. carArray.Sort();
End of Code Snippet


If you observe the contents of the array after the Sort(), you will notice that the elements are sorted alphabetically ie., "BMW," "Corvette," "Honda."

However, consider a Car Class as shown in Listing 2.
Code Snippet
  1. class Car
  2. {
  3.   public string Make { set; get; }
  4.   public int Year { set; get; }
  5.   public string Location { set; get; }
  6. }
End of Code Snippet


If you create an ArrayList of car objects and try to Sort() it, it would throw an exception. You will need to have the Car class implement the IComparable interface and define the CompareTo method to be able to sort custom objects.


Method 1: Implementing the IComparable Interface

Step 1: Implement the IComparable interface
Code Snippet
  1. class Car : IComparable
End of Code Snippet


Step 2: Define the CompareTo method.

In this example we will be sorting by the Make property of the Car Class.
Code Snippet
  1. public int CompareTo(object obj)
  2. {
  3.   if (obj is Car)
  4.   {
  5.     Car c2 = (Car)obj;
  6.     return Make.CompareTo(c2.Make);
  7.   }
  8.   else
  9.     throw new ArgumentException("Object is not of type Car.");
  10. }
End of Code Snippet


That is it! We are now ready to test if our sorting works.

Step 3: Test Sorting by Make.
Code Snippet
  1. Car objCar = new Car();
  2. ArrayList carArray = new ArrayList();
  3.  
  4. objCar.Make = "BMW";
  5. objCar.Year = 2008;
  6. objCar.Location = "Florida";
  7. carArray.Add(objCar);
  8. objCar = null;
  9.  
  10. objCar = new Car();
  11. objCar.Make = "Honda";
  12. objCar.Year = 1996;
  13. objCar.Location = "Illinois";
  14. carArray.Add(objCar);
  15. objCar = null;
  16.  
  17. objCar = new Car();
  18. objCar.Make = "Corvette";
  19. objCar.Year = 2006;
  20. objCar.Location = "California";
  21. carArray.Add(objCar);
  22. objCar = null;
  23.  
  24. carArray.Sort();
End of Code Snippet


You will now observe that the carArray is sorted alphabetically by Make.

Note: Sometimes instead of using the ArrayList, it is possible that you are working with an object array. You can use the C# built in Adapter() method as shown below.

To convert from an array to ArrayList use:
Code Snippet
  1. ArrayList carArray = ArrayList.Adapter(carObjectArray);
End of Code Snippet

To convert from an ArrayList to object array use:
Code Snippet
  1. Car[] carObjectArray = (Car[])carArray.ToArray(typeof(Car));
End of Code Snippet


Method 2: Using the IComparer Interface

Sometimes, it might be necessary to have more flexibility in your sorting, for example, to provide which property you want to sort the Car array by. In situations like this, we would need to use the IComparer interface and use the overloaded Sort method that takes the comparer instance as an argument.

Step 1: Create a CarComparer class that implements the IComparer interface
Code Snippet
  1. class CarComparer: IComparer
  2. {
  3.   public enum ComparisonType
  4.   {
  5.     Make = 1, Year, Location
  6.   }
  7.  
  8.   public ComparisonType ComparisonMethod
  9.   {
  10.     set;
  11.     get;
  12.   }
  13.  
  14.   public int Compare(object x, object y)
  15.   {
  16.     Car c1;
  17.     Car c2;
  18.  
  19.     if (x is Car)
  20.       c1 = x as Car;
  21.     else
  22.       throw new ArgumentException("Object is not of type Car.");
  23.  
  24.     if (y is Car)
  25.       c2 = y as Car;
  26.     else
  27.       throw new ArgumentException("Object is not of type Car.");
  28.  
  29.     return c1.CompareTo(c2, ComparisonMethod);
  30.   }
  31. }
End of Code Snippet


The main purpose of the CarComparer is to keep track of by what property we are sorting. It has a ComparisonType enum that has the property elements and an overloaded Compare method.


Step 2

Add an overloaded CompareTo to the Car class as shown in Listing 7.

Code Snippet
  1. public int CompareTo(Car c2, CarComparer.ComparisonType comparisonType)
  2. {
  3.   switch (comparisonType)
  4.   {
  5.     case CarComparer.ComparisonType.Make:
  6.       return Make.CompareTo(c2.Make);
  7.     case CarComparer.ComparisonType.Year:
  8.       return Year.CompareTo(c2.Year);
  9.     case CarComparer.ComparisonType.Location:
  10.       return Location.CompareTo(c2.Location);
  11.     default:
  12.       return Make.CompareTo(c2.Make);
  13.   }
  14. }
End of Code Snippet


That is it, we are done. We now have added the ability to sort by Make, Year or Location. Let us test it out.

Step 3
Code Snippet
  1. Car objCar = new Car();
  2. ArrayList carArray = new ArrayList();
  3.  
  4. objCar.Make = "BMW";
  5. objCar.Year = 2008;
  6. objCar.Location = "Florida";
  7. carArray.Add(objCar);
  8. objCar = null;
  9. objCar = new Car();
  10. objCar.Make = "Honda";
  11. objCar.Year = 1996;
  12. objCar.Location = "Illinois";
  13. carArray.Add(objCar);
  14. objCar = null;
  15.  
  16. objCar = new Car();
  17. objCar.Make = "Corvette";
  18. objCar.Year = 2006;
  19. objCar.Location = "ZZ";
  20. carArray.Add(objCar);
  21. objCar = null;
  22.  
  23. CarComparer carComparer = new CarComparer();
  24. carComparer.ComparisonMethod = CarComparer.ComparisonType.Location;
  25. carArray.Sort(carComparer);
End of Code Snippet


We use the overloaded Sort method that takes an instance of the CarComparer class. To sort by other properties, change the ComparisonMethod as shown below.

Code Snippet
  1. carComparer.ComparisonMethod = CarComparer.ComparisonType.Year
End of Code Snippet



Conclusion
This article demonstrates different ways of sorting an array of custom objects. It provides a step-by-step approach in using the IComparable interface and in creating a Comparer class for sorting.

1 comment:

XL said...

Very useful, thanks!