.Net provides a method sort() for sorting a list that contains a primitive data type, but for a list that contains a complex data type, the sort() method is not working.In the blog post, we will see how to sort a list containing complex data types.
Getting StartedThe sort method of generic collection in .Net works for a list that contains primitive or simple data types, it is very straightforward to invoke the Sort() method on the list instance and the data will be automatically sorted in ascending order.
Sorting List of Primitive Types
For example, if a list has data type int, double string or char, etc. then that list can be sorted by invoking the sort() method directly on the instance. the following code examples describe how to use the sort() method with a list of simple types.
List of Integer
The following source code sorts an integer list and displays the result in the console window.
List<int> list = new List<int> { 5, 7, 3, 8, 9, 4, 2, 1, 6 };
list.Sort();
Console.WriteLine("Result after sorting");
foreach (int x in list)
{
Console.WriteLine(x);
}
Result
Result after sorting
1
2
3
4
5
6
7
8
9
List of String
The following source code sorts a string list and displays the result in the console window.
List<string> stringList = new List<string>() { "Raman", "Krishna", "Shyam", "Baishnav", "Asutosh" };
stringList.Sort();
Console.WriteLine("Result after sorting");
foreach (string s in stringList)
{
Console.WriteLine(s);
}
Result
Result after sorting
Asutosh
Baishnav
Krishna
Raman
Shyam
But in the case of a complex type list is not straightforward. if the sort() method is invoked on the instance of a complex type list then in run time throw “Invalid operation exception – Failed to compare two elements in the array“because it does not understand how would sort the list.
For example, if a list contains a collection of class objects if we invoke the sort() method on the instance of the list then the run time will throw the above-mentioned exception because the run does not understand whether comparison would apply on the instance of the class or to the properties of the class object.
Use the following code example to demonstrate how the sort method throws an exception.
List<Student> students = new List<Student>();
students.Add(new Student { ID = 1, Name = "Rajesh", Class = 1, Mark = 89 });
students.Add(new Student { ID = 2, Name = "Ravindar", Class = 3, Mark = 98 });
students.Add(new Student { ID = 3, Name = "Yoshna", Class = 2, Mark = 79 });
students.Sort();
Result
Sorting A List |
In the above case, the .NET Runtime did not identify whether to sort the data based on ID, Name, or Class, or based on Mark property, or a combination of any properties.
Solution of Sorting A List
To sort out this problem the .Net Framework introduced the IComparable Interface where we can explicitly mention to which property the run time will compare.
IComparable Interface
IComparable interface defines a generalized type-specific comparison method that a value type or class implements to order or sort its instances.
The following code example illustrates to use the IComparable interface and sort the list by comparing the Mark property.
class Program
{
static void Main(string[] args)
{
try
{
List<Student> students = new List<Student>();
students.Add(new Student { ID = 1, Name = "Rajesh", Class = 1, Mark = 89 });
students.Add(new Student { ID = 2, Name = "Ravindar", Class = 3, Mark = 98 });
students.Add(new Student { ID = 3, Name = "Yoshna", Class = 2, Mark = 79 });
Console.WriteLine("Student details before sorting");
foreach(Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
Console.WriteLine();
students.Sort();
Console.WriteLine("Student details after sorting");
foreach (Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
public class Student : IComparable<Student>
{
public int ID { get; set; }
public string Name { get; set; }
public int Class { get; set; }
public float Mark { get; set; }
public int CompareTo(Student other)
{
return this.Mark.CompareTo(other.Mark);
}
}
}
Result
Student details before sorting
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :2, Name:Ravindar, Class: 3, Mark:98
ID :3, Name:Yoshna, Class: 2, Mark:79
Student details after sorting
ID :3, Name:Yoshna, Class: 2, Mark:79
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :2, Name:Ravindar, Class: 3, Mark:98
In the CompareTo function you can compare any properties of complex type and own logic can be writen to provide ordering of your class on several fields or properties, ascending and descending order on the same field, or both like below.
Ascending Order
public int CompareTo(Student other)
{
if (this.Mark > other.Mark)
return 1;
if (this.Mark < other.Mark)
return -1;
else
return 0;
}
Descending Order
public int CompareTo(Student other)
{
if (this.Mark < other.Mark)
return 1;
if (this.Mark > other.Mark)
return -1;
else
return 0;
}
Problem:-
Problem with the IComparable Interface is that you can implement the IComparable interface when you have access of the Student class to modify it. Asume that you have provided a library that is built by third pary and the student class is inside the library. In this case you don't have access to the class to modify then how will sort a list that you created using the student class.
To overcome this .Net Framework provides another interface that is call IComparer Interface which provides that capability by requiring a class to provide Compare() method. Where you can create you own class by implementing the IComparer interface and use it to sort a list of complex class. See the blow code example to know more it.
IComparer Interface
The following example demonstrates the use of the IComparer interface to sort a list. In this example, the Compare method is implemented using the AscendingOrderHelper class to order the list in asending order and DescendingHelper class to reverse the order of the contents of the List.
class Program
{
static void Main(string[] args)
{
try
{
List<Student> students = new List<Student>();
students.Add(new Student { ID = 1, Name = "Rajesh", Class = 1, Mark = 89 });
students.Add(new Student { ID = 2, Name = "Ravindar", Class = 3, Mark = 98 });
students.Add(new Student { ID = 3, Name = "Yoshna", Class = 2, Mark = 79 });
Console.WriteLine("Student details before sorting");
foreach(Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
Console.WriteLine();
AscendingOrderHelper ascendingOrder = new AscendingOrderHelper();
students.Sort(ascendingOrder);
Console.WriteLine("Student details after sorting for ascending order");
foreach (Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
Console.WriteLine();
DescendingOrderHelper descendingOrder = new DescendingOrderHelper();
students.Sort(descendingOrder);
Console.WriteLine("Student details after sorting for descending order");
foreach (Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
public class AscendingOrderHelper : IComparer<Student>
{
public int Compare(Student x, Student y)
{
if (x.Mark > y.Mark)
return 1;
if (x.Mark < y.Mark)
return -1;
else
return 0;
}
}
public class DescendingOrderHelper : IComparer<Student>
{
public int Compare(Student x, Student y)
{
if (x.Mark < y.Mark)
return 1;
if (x.Mark > y.Mark)
return -1;
else
return 0;
}
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int Class { get; set; }
public float Mark { get; set; }
}
}
Result
Student details before sorting
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :2, Name:Ravindar, Class: 3, Mark:98
ID :3, Name:Yoshna, Class: 2, Mark:79
Student details after sorting for ascending order
ID :3, Name:Yoshna, Class: 2, Mark:79
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :2, Name:Ravindar, Class: 3, Mark:98
Student details after sorting for descending order
ID :2, Name:Ravindar, Class: 3, Mark:98
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :3, Name:Yoshna, Class: 2, Mark:79
Note:-
Let's way you want create a logic where you will write comparison logic insted of creating a class by implementing IComparer interface to sort a complex type list. But problem is that a function can not be passed as parameter to sort() method. To help you in this way .Net Framework also introducted the Comparision Delegate. see the blow code example to know how to use the Comparision Delegate.
Comparision Delegate
The following code example illustrates how to use of Comparision Delegate and represend the list with ascending order of class property.
class Program
{
static void Main(string[] args)
{
try
{
List<Student> students = new List<Student>();
students.Add(new Student { ID = 1, Name = "Rajesh", Class = 1, Mark = 89 });
students.Add(new Student { ID = 2, Name = "Ravindar", Class = 3, Mark = 98 });
students.Add(new Student { ID = 3, Name = "Yoshna", Class = 2, Mark = 79 });
Console.WriteLine("Student details before sorting");
foreach(Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
Console.WriteLine();
Comparison<Student> classcomparision = new Comparison<Student>(CompareClass);
students.Sort(classcomparision);
Console.WriteLine("Student details after sorting");
foreach (Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
public static int CompareClass(Student s1, Student s2)
{
return s1.Class.CompareTo(s2.Class);
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int Class { get; set; }
public float Mark { get; set; }
}
}
Result
Student details before sorting
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :2, Name:Ravindar, Class: 3, Mark:98
ID :3, Name:Yoshna, Class: 2, Mark:79
Student details after sorting
ID :1, Name:Rajesh, Class: 1, Mark:89
ID :3, Name:Yoshna, Class: 2, Mark:79
ID :2, Name:Ravindar, Class: 3, Mark:98
Note:-
You can use the name of the function in sort method() without using parameter insted of delegate. see the below exampel. Here in this case .Net Framework will use a delegate internally to call your method.
class Program
{
static void Main(string[] args)
{
try
{
List<Student> students = new List<Student>();
students.Add(new Student { ID = 1, Name = "Rajesh", Class = 1, Mark = 89 });
students.Add(new Student { ID = 2, Name = "Ravindar", Class = 3, Mark = 98 });
students.Add(new Student { ID = 3, Name = "Yoshna", Class = 2, Mark = 79 });
Console.WriteLine("Student details before sorting");
foreach(Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
Console.WriteLine();
students.Sort(CompareClass);
Console.WriteLine("Student details after sorting");
foreach (Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
public static int CompareClass(Student s1, Student s2)
{
return s1.Class.CompareTo(s2.Class);
}
public class Student
{
public int ID { get; set; }
public string Name { get; set; }
public int Class { get; set; }
public float Mark { get; set; }
}
}
Problem:-
The above logics will be fine for the multiple use to avoid writing multiple codes. but what about if the comparison logic is required only one time. In this case ofcourse someone will avoid to write lots of code to implement IComparable or IComparer or use of Comparision Delegate. In this scenario the anonous function can be used where you can write comparison login in one or two lines of code like below code example.
The following code example illustrates how to use of anonymous function and represend the list with ascending order of class property.
static void Main(string[] args)
{
try
{
List<Student> students = new List<Student>();
students.Add(new Student { ID = 1, Name = "Rajesh", Class = 1, Mark = 89 });
students.Add(new Student { ID = 2, Name = "Ravindar", Class = 3, Mark = 98 });
students.Add(new Student { ID = 3, Name = "Yoshna", Class = 2, Mark = 79 });
Console.WriteLine("Student details before sorting");
foreach(Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
Console.WriteLine();
students.Sort((s1,s2)=>s1.Class.CompareTo(s2.Class));
Console.WriteLine("Student details after sorting");
foreach (Student s in students)
{
Console.WriteLine("ID :{0}, Name:{1}, Class: {2}, Mark:{3}", s.ID, s.Name, s.Class, s.Mark);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
Console.ReadLine();
}
The sort() function works with the simple type list becuase the simple data type alrady implements IComparable and IComparer interface but the user defined data type or complex data have to use the IComparable or IComparer or Comparision delegate to sort a list.
Summary
In the above discussion, we learned in which scenario the IComparable Interface, IComparer Interface and the Comparision Delegate is usable for sorting a list of complext type. I hope you have enjoyed it and this blog post is helpful to you.
Thanks
Wow interesting....
ReplyDelete