How to sort a list of objects with IComparable and IComparer

14,906

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));

See this List.Sort overload.

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();
      }
   }
}
Share:
14,906
Maximus Decimus
Author by

Maximus Decimus

Updated on July 29, 2022

Comments

  • Maximus Decimus
    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
    Maximus Decimus over 9 years
    The 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
    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
    GregRos over 9 years
    As a sidenote, you can always get the IComparer<T> of a type implementing IComparable<T> using the static property Comparer<T>.Default.