How to sort a list of objects with IComparable and IComparer
Solution 1
First, there's no need to have an IComparer<Employee>
that sorts by descending if your Employee
class implements IComparable<Employee>
using the same sort criteria. And it's horribly inefficient for your Employee
class to instantiate a new IComparer<Employee>
for every comparision.
You should change your Employee
class so that its CompareTo
looks like this:
int CompareTo(Employee next)
{
return next.NumberOfKids.CompareTo(this.NumberOfKids);
}
Then you can ditch the EmployeeComparer
altogether and sort like this:
list = list.Take(3).ToList();
list.Sort(); // Uses default IComparable for the Employee class
return list;
Typically, you make the IComparable<T>
implementation on the class perform the default sorting order. In the case of employees, that'd probably either be by employee ID or perhaps last name, first name. IComparer<T>
implementations should be for other sorting criteria.
With List<T>
, though, you have another option: use an anonymous function. For example, you could do this by writing:
list.Sort((x, y) => y.NumberOfKids.CompareTo(x.NumberOfKids));
Or, you could just ditch the whole idea of IComparer<T>
and IComparable<T>
and List.Sort
altogether and do it the LINQ way:
var result = list.Take(3).OrderByDescending(x => x.NumberOfKids).ToList();
Solution 2
here is another way got from msdn
using System;
using System.Collections;
namespace ConsoleEnum
{
public class car : IComparable
{
// Beginning of nested classes.
// Nested class to do ascending sort on year property.
private class sortYearAscendingHelper: IComparer
{
int IComparer.Compare(object a, object b)
{
car c1=(car)a;
car c2=(car)b;
if (c1.year > c2.year)
return 1;
if (c1.year < c2.year)
return -1;
else
return 0;
}
}
// Nested class to do descending sort on year property.
private class sortYearDescendingHelper: IComparer
{
int IComparer.Compare(object a, object b)
{
car c1=(car)a;
car c2=(car)b;
if (c1.year < c2.year)
return 1;
if (c1.year > c2.year)
return -1;
else
return 0;
}
}
// Nested class to do descending sort on make property.
private class sortMakeDescendingHelper: IComparer
{
int IComparer.Compare(object a, object b)
{
car c1=(car)a;
car c2=(car)b;
return String.Compare(c2.make,c1.make);
}
}
// End of nested classes.
private int year;
private string make;
public car(string Make,int Year)
{
make=Make;
year=Year;
}
public int Year
{
get {return year;}
set {year=value;}
}
public string Make
{
get {return make;}
set {make=value;}
}
// Implement IComparable CompareTo to provide default sort order.
int IComparable.CompareTo(object obj)
{
car c=(car)obj;
return String.Compare(this.make,c.make);
}
// Method to return IComparer object for sort helper.
public static IComparer sortYearAscending()
{
return (IComparer) new sortYearAscendingHelper();
}
// Method to return IComparer object for sort helper.
public static IComparer sortYearDescending()
{
return (IComparer) new sortYearDescendingHelper();
}
// Method to return IComparer object for sort helper.
public static IComparer sortMakeDescending()
{
return (IComparer) new sortMakeDescendingHelper();
}
}
}
and here is how to use
using System;
namespace ConsoleEnum
{
class host
{
[STAThread]
static void Main(string[] args)
{
// Create an arary of car objects.
car[] arrayOfCars= new car[6]
{
new car("Ford",1992),
new car("Fiat",1988),
new car("Buick",1932),
new car("Ford",1932),
new car("Dodge",1999),
new car("Honda",1977)
};
// Write out a header for the output.
Console.WriteLine("Array - Unsorted\n");
foreach(car c in arrayOfCars)
Console.WriteLine(c.Make + "\t\t" + c.Year);
// Demo IComparable by sorting array with "default" sort order.
Array.Sort(arrayOfCars);
Console.WriteLine("\nArray - Sorted by Make (Ascending - IComparable)\n");
foreach(car c in arrayOfCars)
Console.WriteLine(c.Make + "\t\t" + c.Year);
// Demo ascending sort of numeric value with IComparer.
Array.Sort(arrayOfCars,car.sortYearAscending());
Console.WriteLine("\nArray - Sorted by Year (Ascending - IComparer)\n");
foreach(car c in arrayOfCars)
Console.WriteLine(c.Make + "\t\t" + c.Year);
// Demo descending sort of string value with IComparer.
Array.Sort(arrayOfCars,car.sortMakeDescending());
Console.WriteLine("\nArray - Sorted by Make (Descending - IComparer)\n");
foreach(car c in arrayOfCars)
Console.WriteLine(c.Make + "\t\t" + c.Year);
// Demo descending sort of numeric value using IComparer.
Array.Sort(arrayOfCars,car.sortYearDescending());
Console.WriteLine("\nArray - Sorted by Year (Descending - IComparer)\n");
foreach(car c in arrayOfCars)
Console.WriteLine(c.Make + "\t\t" + c.Year);
Console.ReadLine();
}
}
}
Maximus Decimus
Updated on July 29, 2022Comments
-
Maximus Decimus almost 2 years
I'm trying to implement the same example of this link but more oriented on the number of dependent kids.
http://www.codeproject.com/Articles/42839/Sorting-Lists-using-IComparable-and-IComparer-Inte
So I have 3 employees with A: 0, B: 0, C: 2 respectively. I want to order them descendently by number of kids. So I would have C:2, B:0, A:0
But my list it's not being sorted. It remains as A: 0, B: 0, C: 2
What I'm doing wrong?
My Comparer
public class EmployeeComparer : IComparer<Employee> { public int Compare(Employee x, Employee y) { this.CompareNumberOfKids(x, y); } public int CompareNumberOfKids(Employee x, Employee y) { if (x.NumberOfKids > y.NumberOfKids) { return -1; } else if (x.NumberOfKids < y.NumberOfKids) { return 1; } else { return 0; } } }
My Business Entity
public class Employee : IComparable<Employee> { //...// Public NumberOfKids { get; set; } int IComparable<Employee>.CompareTo(Employee next) { return new EmployeeComparer().Compare(this, next); } public override bool Equals(object obj) { if (obj != null && obj is Emmployee) { return ((Employee)obj).ID.Equals(this.ID); } else return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); } }
Aspx.cs
public List<Employee> GetEmployeeSortedList() { List<Employee> list = new List<Employee>(); list.Add(new Employee() { Name = "A", NumberOfKids = 0 } ); list.Add(new Employee() { Name = "B", NumberOfKids = 0 } ); list.Add(new Employee() { Name = "C", NumberOfKids = 2 } ); list.Add(new Employee() { Name = "D", NumberOfKids = 1 } ); list.Add(new Employee() { Name = "E", NumberOfKids = 0 } ); list.Add(new Employee() { Name = "F", NumberOfKids = 4 } ); list = list.Take(3).ToList(); EmployeeComparer comp = new EmployeeComparer(); list.Sort(comp); return list; }
-
Maximus Decimus over 9 yearsThe last line worked fine for me. But I implemented a new class IComparer in case of evaluating other alternatives more than NumberOfKids in a collection. Can I delegate the int CompareTo to a class that doesn't implements IComparer?
-
Jim Mischel over 9 years@MaximusDecimus: I don't know what you mean by "delegate the int CompareTo". Take a look at informit.com/guides/content.aspx?g=dotnet&seqNum=781 and informit.com/guides/content.aspx?g=dotnet&seqNum=782, and informit.com/guides/content.aspx?g=dotnet&seqNum=783. Perhaps those will shed some light on the subject.
-
GregRos over 9 yearsAs a sidenote, you can always get the
IComparer<T>
of a type implementingIComparable<T>
using the static propertyComparer<T>.Default
.