Sorting out your problems
[Intermediate]
I made a small but cute class for Person - there's Name, which identifies the person's name and there's Age, which represents the person's age.
Creating multiple instances of the persons class would require me to kick them all into an ArrayList - which I did. I had a precise idea in mind: I wanted the ArrayList to sort my list of Persons by either name or age.
This is when I learnt about the IComparer and IComparable interfaces. However, in this tutorial, we're going to focus on the IComparer interface. Please note that the IComparer interface requires the use of System.Collections. Below is the code for my Person class.
/// <summary>
/// The Person class contains the name of the person and the age of
/// the person.
/// </summary>
public class Person
{
//private string holding person's name
private string name;
//private int holding person's age
private int age;
/// <summary>
/// Creates a new Person object with the specified name and age.
/// </summary>
/// <param name="name">The name of the person</param>
/// <param name="age">The age of the person</param>
public Person(string name, int age)
{
this.name = name;
this.age - age;
}
/// <summary>
/// Gets or sets the name of the person
/// </summary>
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
}
}
/// <summary>
/// Gets or sets the age of the person
/// </summary>
public int Age
{
get
{
return this.age;
}
set
{
this.age = value;
}
}
}
A class implementing the IComparer interface can be passed onto the ArrayList.Sort method to provide the latter with a means of comparing two values. Why compare two values? Simple, because the QuickSort algorithm (which the arraylist obviously implements) works by consecutively comparing two values. Any quicksort algorithm can easily be adapted on a set of data, given the comparing method.
What's left to do is to create a class which implements the IComparer method. Also, we might wish to allow for "customization" of that class, i.e. allow different means of sorting. To do this, I created an enumerated type which would hold the different types of sorting. See the code below:
/// <summary>
/// Enumerator for types of person sorting
/// </summary>
public enum SortPersonsBy {byName, byAge};
/// <summary>
/// Person Sorting Class, implements IComparer
/// </summary>
public class SortPersons : IComparer
{
//Private value holding the Persons sort type
private SortPersonsBy sortType;
/// <summary>
/// Creates a new instance of the SortPersons class. Use this class to specify sorting
/// methods for arraylists filled with only persons.
/// </summary>
/// <param name="sortingType"></param>
public SortPersons(SortPersonsBy sortingType)
{
this.sortType = sortingType;
}
/// <summary>
/// Gets or sets the SortPersonsBy type.
/// </summary>
public SortPersonsBy SortType
{
get
{
return this.sortType;
}
set
{
this.sortType = value;
}
}
/// <summary>
/// Implementation of the Compare method, required for the IComparer class
/// </summary>
/// <param name="x">First object</param>
/// <param name="y">Second object</param>
/// <returns>An int, which is the value obtained when the two objects
/// have been compared.</returns>
public int Compare(object x, object y)
{
//Check whether x and y are both Person classes.
if ((x is Person) && (y is Person))
{
//Some little typecasting
Person a = (Person)x;
Person b = (Person)y;
//Check the sorting type
if (this.sortType == SortPersonsBy.byName)
{
//Use the native string.CompareTo method
return (a.Name.CompareTo(b.Name));
}
else
{
//Use the native int.CompareTo method
return (a.Age.CompareTo(b.Age));
}
}
else
{
//return 0 if x or y are of wrong type
return 0;
}
}
}
The code above contains an enumerated type and a SortPersons class, which implements the IComparer interface. The SortPersons class has a SortPersonsBy property, which allows the user to choose between sorting by name and sorting by age.
When the ArrayList.Sort() method is called with a SortPersons class, the Compare method is executed numerous times. First of all, my Compare method checks for the types of objects - i.e. whether they are persons or not. If one of them isn't a Person class, the Compare method returns 0. If they both are Person classes, the type of sorting is checked. If the type of sorting is set to "byName", the string's CompareTo method is called using the two person's names; if the type of sorting is set to "byAge", the int's CompareTo method is called using the two person's ages.
And that's it! Here's the full source code of my app:
using System;
using System.Collections;
namespace SortProject
{
/// <summary>
/// The Main Class
/// </summary>
class MainClass
{
/// <summary>
/// Con. App. Entry Pt.
/// </summary>
[STAThread]
static void Main(string[] args)
{
//Create a few persons
Person a = new Person("Rowan", 19);
Person b = new Person("Rishi", 19);
Person c = new Person("Vidisha", 18);
Person d = new Person("Dominique", 21);
Person e = new Person("Arvind", 22);
Person f = new Person("Mom", 49);
Person g = new Person("Visitor", -1);
//Create a SortPersons object, and set the sorting type to "byName"
SortPersons mySortingMethod = new SortPersons(SortPersonsBy.byName);
//Create a new arraylist and kick in the persons
ArrayList someList = new ArrayList();
someList.Add(a);
someList.Add(b);
someList.Add(c);
someList.Add(d);
someList.Add(e);
someList.Add(f);
someList.Add(g);
someList.Sort(mySortingMethod);
Console.WriteLine("Sorting persons by name: \n\n");
//Display the list of persons
for (int i=0; i<= someList.Count-1; i++)
{
Person somePerson = ((Person)someList[i]);
Console.WriteLine("Person Name: " + somePerson.Name +
"\t\tPerson Age: " + somePerson.Age);
}
Console.WriteLine("Sorting the persons by age: \n\n");
//Set the sorting type to "byAge" and resort the ilst
mySortingMethod.SortType = SortPersonsBy.byAge;
someList.Sort(mySortingMethod);
//Re-Display the list of persons
for (int i=0; i<= someList.Count-1; i++)
{
Person somePerson = ((Person)someList[i]);
Console.WriteLine("Person Name: " + somePerson.Name +
"\t\tPerson Age: " + somePerson.Age);
}
Console.ReadLine();
}
}
/// <summary>
/// The Person class contains the name of the person and the age of
/// the person.
/// </summary>
public class Person
{
//private string holding person's name
private string name;
//private int holding person's age
private int age;
/// <summary>
/// Creates a new Person object with the specified name and age.
/// </summary>
/// <param name="name">The name of the person</param>
/// <param name="age">The age of the person</param>
public Person(string name, int age)
{
this.name = name;
this.age = age;
}
/// <summary>
/// Gets or sets the name of the person
/// </summary>
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
}
}
/// <summary>
/// Gets or sets the age of the person
/// </summary>
public int Age
{
get
{
return this.age;
}
set
{
this.age = value;
}
}
}
/// <summary>
/// Enumerator for types of person sorting
/// </summary>
public enum SortPersonsBy {byName, byAge};
/// <summary>
/// Person Sorting Class, implements IComparer
/// </summary>
public class SortPersons : IComparer
{
//Private value holding the Persons sort type
private SortPersonsBy sortType;
/// <summary>
/// Creates a new instance of the SortPersons class. Use this class to specify sorting
/// methods for arraylists filled with only persons.
/// </summary>
/// <param name="sortingType"></param>
public SortPersons(SortPersonsBy sortingType)
{
this.sortType = sortingType;
}
/// <summary>
/// Gets or sets the SortPersonsBy type.
/// </summary>
public SortPersonsBy SortType
{
get
{
return this.sortType;
}
set
{
this.sortType = value;
}
}
/// <summary>
/// Implementation of the Compare method, required for the IComparer class
/// </summary>
/// <param name="x">First object</param>
/// <param name="y">Second object</param>
/// <returns>An int, which is the value obtained when the two objects
/// have been compared.</returns>
public int Compare(object x, object y)
{
//Check whether x and y are both Person classes.
if ((x is Person) && (y is Person))
{
//Some little typecasting
Person a = (Person)x;
Person b = (Person)y;
//Check the sorting type
if (this.sortType == SortPersonsBy.byName)
{
//Use the native string.CompareTo method
return (a.Name.CompareTo(b.Name));
}
else
{
//Use the native int.CompareTo method
return (a.Age.CompareTo(b.Age));
}
}
else
{
//return 0 if x or y are of wrong type
return 0;
}
}
}
}
# posted by Rowy @ Thursday, July 14, 2005