Using IComparer / IComparable for Sorting

Posted: September 8, 2010 in Dotnet basics

IComparer / IComparable are interfaces used to compare two objects. The implementing class has to define the logic for sorting. It provides a way to customize the sort order of a collection.

namespace WpfApplication1
{
///

/// Interaction logic for MainWindow.xaml
///

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

// Exception as there is no IComparable implementation for Sorting. Exception message – Failed to compare two elements in the array
List empList = new List();
empList.AddRange(new List(){new Emp { Name = "A", Age = 25, Address = "Test StreetA" },
new Emp { Name = "A", Age = 26, Address = "Test StreetA1" },
new Emp { Name = "B", Age = 25, Address = "Test StreetB" },
new Emp { Name = "C", Age = 26, Address = "Test StreetC" },
new Emp { Name = "Z", Age = 25, Address = "Test StreetZ" },
new Emp { Name = "D", Age = 26, Address = "Test StreetD" }});
empList.Sort();

}
}

public class Emp
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }
}

Implementation of IComparable in WPF. Here, we implement the CompareTo method and sort the Emp objects based on Age property.

namespace WpfApplication1
{
///

/// Interaction logic for MainWindow.xaml
///

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

// Sorting using IComparable based Age ascending order
List empList = new List();
empList.AddRange(new List(){new Emp { Name = "A25", Age = 25, Address = "Test StreetA25" },
new Emp { Name = "B35", Age = 35, Address = "Test StreetB35" },
new Emp { Name = "C15", Age = 15, Address = "Test StreetC15" },
new Emp { Name = "D45", Age = 45, Address = "Test StreetD45" },
new Emp { Name = "E5", Age = 5, Address = "Test StreetE5" }});
empList.Sort();

foreach (dynamic emp in empList)
{
string listTemp = emp.Name + "-" + emp.Age + "-" + emp.Address;
myListBox.Items.Add(listTemp);
}

}
}

public class Emp : IComparable
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }

#region IComparable Members

public int CompareTo(Emp other)
{
// Sort based on Age
if (this.Age > other.Age)
{
return 1;
}
else if (other.Age > this.Age)
return -1;
else
return 0;

}

#endregion
}
}

Note::

List test = new List();
test.Add("B");
test.Add("C");
test.Add("A");
test.Sort();

This will work because by default List and Array implement IComparable.
But below example this won’t work.

public class Emp
{
public string Name { get; set; }
}

// Sorting using IComparable based Age ascending order
List empList = new List();
empList.AddRange(new List(){new Emp { Name = "C25"}, new Emp { Name = "B35"},new Emp { Name = "A15"}});

empList.Sort();

C# compiler is not smart enough to figure out which instance of type “Emp” should come first etc.

IComparer interface :

But sometimes, we may need to sort a list of objects when class does not implement IComparable<> interface and also we may need various kinds of sorting on that class like:

1. Sort Emp by Age in Ascending Order
2. Sort Emp by Age in Descending Order
3. Sort Emp by Name

To solve this problem, .NET provides a special interface called IComparer<> which has a method Compare(), takes two object parameters X, Y and returns an int. Use of IComparer<> interface tells List how exactly you want to sort.

namespace WpfApplication1
{
///

/// Interaction logic for MainWindow.xaml
///

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

// Sorting using IComparable based Age ascending order
List empList = new List();
empList.AddRange(new List(){new Emp { Name = "A25", Age = 25, Address = "Test StreetA25" },
new Emp { Name = "B35", Age = 35, Address = "Test StreetB35" },
new Emp { Name = "C15", Age = 15, Address = "Test StreetC15" },
new Emp { Name = "D45", Age = 45, Address = "Test StreetD45" },
new Emp { Name = "E5", Age = 5, Address = "Test StreetE5" }});

// Default sort as defined by IComparable
empList.Sort();

foreach (dynamic emp in empList)
{
string listTemp = emp.Name + "-" + emp.Age + "-" + emp.Address;
myListBox.Items.Add(listTemp);
}

// Create IComparer instance
Emp_SortByAgeByDescendingOrder descSort = new Emp_SortByAgeByDescendingOrder();

// Specify the type
empList.Sort(descSort);

foreach (dynamic emp in empList)
{
string listTemp = emp.Name + "-" + emp.Age + "-" + emp.Address;
myListBox1.Items.Add(listTemp);
}

}
}

public class Emp : IComparable
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }

public int CompareTo(Emp other)
{
// Sort based on Age
if (this.Age > other.Age)
{
return 1;
}
else if (other.Age > this.Age)
return -1;
else
return 0;

}

}

class Emp_SortByAgeByDescendingOrder : IComparer
{
#region IComparer Members

public int Compare(Emp x, Emp y)
{
if (x.Age < y.Age) return 1;
else if (x.Age > y.Age) return -1;
else return 0;
}

#endregion
}

class Emp_SortByName : IComparer
{
#region IComparer Members

public int Compare(Emp x, Emp y)
{
return string.Compare(x.Name, y.Name);
}

#endregion
}
}

Special case :: If we have both IComparer and IComparable implemented

Sort will take place based on IComparer logic first and then followed by IComparable logic.

public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();

List empList = new List();
empList.AddRange(new List(){new Emp { Name = "A", Age = 25, Address = "Test StreetA25" },
new Emp { Name = "A", Age = 35, Address = "Test StreetB35" },
new Emp { Name = "B", Age = 15, Address = "Test StreetC15" },
new Emp { Name = "B", Age = 45, Address = "Test StreetD45" },
new Emp { Name = "C", Age = 5, Address = "Test StreetE5" }});

// Create IComparer instance
// IComparer sorts by Name asc
// IComparable sorts by Age desc
// Sort should be Name asc and Age desc
Emp_SortByName descSort = new Emp_SortByName();

// Specify the type
empList.Sort(descSort);

foreach (dynamic emp in empList)
{
string listTemp = emp.Name + "-" + emp.Age + "-" + emp.Address;
myListBox1.Items.Add(listTemp);
}

}
}

public class Emp : IComparable
{
public string Name { get; set; }
public int Age { get; set; }
public string Address { get; set; }

public int CompareTo(Emp other)
{
// Sort based on Age
if (this.Age > other.Age)
{
return 1;
}
else if (other.Age > this.Age)
return -1;
else
return 0;

}

}

class Emp_SortByName : IComparer
{
#region IComparer Members

public int Compare(Emp x, Emp y)
{
return string.Compare(x.Name, y.Name);
}

#endregion
}

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s