locked
Sorting Complex Type Using IComparer RRS feed

  • Question

  • User1231829591 posted

    Hi, I am trying to sort my employees based on ManagementLevel by implementing IComplarer interface. However, for the Compare method of the IComparer interface to work a separate class must be created.

    The following is my Employee class definition which implements IComparer's Compare method:

        public class Employee : IComparer<Employee>
        {
            public string Name
            {
                get;
                set;
            }
            public int Salary
            {
                get;
                set;
            }
            public  ManagementLevel ManageLvl
            {
                get; 
                set;
            }
    
            public int Compare(Employee emp, Employee otherEmp)
            {            
                return emp.ManageLvl.CompareTo(otherEmp.ManageLvl);
            }
    
        }

    The following is my Enum definition

     public enum ManagementLevel
        {
            CEO = 1,
            CFO = 2,
            CTO = 3,
            RegionalManager = 4       
        }

    The following adds , sorts, then reverse sorts the list of employees:

    List<Employee> employeeList = new List<Employee>();
    employee emp = new Employee();            
    emp.Name = txtName.Text;
    emp.Salary = Convert.ToInt32(txtSalary.Text);
    emp.ManageLvl = (ManagementLevel)Enum.Parse(typeof(ManagementLevel), txtManagementLevel.Text);
    employeeList.Add(emp);
    
    employeeList.Sort();
    employeeList.Reverse();

    I am getting the error below

    At least one object must implement IComparable.

    As stated above the Sort method does not work. I was only able to make the Compare method of the ICompare interface sort the way I want by creating a separate class but why can't I implement it in the Employee class, thanks in advance for your reply.

    Tuesday, January 12, 2016 5:59 AM

Answers

  • User-434868552 posted

    @ManyTitles

    TIMTOWTDI

    use IComparable instead of IComparer

    this code which works in LINQPad5 is mostly your code from above with fixed typos.   http://linqpad.net  

    public enum ManagementLevel
    {
        CEO = 1,
        CFO = 2,
        CTO = 3,
        RegionalManager = 4
    }
    
    public class Employee : IComparable<Employee>
    {
        public string Name{ get; set; }
        public int Salary{ get; set; }
        public ManagementLevel ManageLvl{ get; set; }
    
        public int CompareTo(Employee employee)
        {
            return this.ManageLvl.CompareTo(employee.ManageLvl);
        }
    }
    void Main()
    {
        List<Employee> employeeList = new List<Employee>();
        
        Employee emp = new Employee();
        emp.Name = "ManyTitles";  emp.Salary = 1234;
        emp.ManageLvl = ManagementLevel.RegionalManager;
        employeeList.Add(emp);
        
        Employee cto = new Employee();
        cto.Name = "CTO";  cto.Salary = 2345;
        cto.ManageLvl = ManagementLevel.CTO;
        employeeList.Add(cto);
    
        Employee ceo = new Employee();
        ceo.Name = "CEO";  ceo.Salary = 123456;
        ceo.ManageLvl = ManagementLevel.CEO;
        employeeList.Add(ceo);
    
        Employee cfo = new Employee();
        cfo.Name = "CFO";  cfo.Salary = 99999;
        cfo.ManageLvl = ManagementLevel.CFO;
        employeeList.Add(cfo);
        
        employeeList.Dump("unordered");
        employeeList.Sort();
        employeeList.Dump("ordered");
        employeeList.Reverse();
        employeeList.Dump("reversed");
    }

    The .Dump method is provided by LINQPad5

    output:

    unordered

    List<Employee> (4 items)
    Name Salary ManageLvl
    ManyTitles 1234 RegionalManager
    CTO 2345 CTO
    CEO 123456 CEO
    CFO 99999 CFO


    ordered

    List<Employee> (4 items)
    Name Salary ManageLvl
    CEO 123456 CEO
    CFO 99999 CFO
    CTO 2345 CTO
    ManyTitles 1234 RegionalManager


    reversed

    List<Employee> (4 items)
    Name Salary ManageLvl
    ManyTitles 1234 RegionalManager
    CTO 2345 CTO
    CFO 99999 CFO
    CEO 123456 CEO

    References:

    https://support.microsoft.com/en-us/kb/320727 "How to use the IComparable and IComparer interfaces in Visual C#"

    http://www.codedigest.com/Articles/CSHARP/84_Sorting_in_Generic_List.aspx "Sorting in C# Generic List(List<T>)"

    it is not working without creating a separate class

    From the first reference above:

    The role of IComparer is to provide additional comparison mechanisms. ...

    Using IComparer is a two-step process.

    First, declare a class that implements IComparer,
    and then implement the Compare method

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, January 12, 2016 9:12 AM

All replies

  • User-434868552 posted

    @ManyTitles

    TIMTOWTDI

    use IComparable instead of IComparer

    this code which works in LINQPad5 is mostly your code from above with fixed typos.   http://linqpad.net  

    public enum ManagementLevel
    {
        CEO = 1,
        CFO = 2,
        CTO = 3,
        RegionalManager = 4
    }
    
    public class Employee : IComparable<Employee>
    {
        public string Name{ get; set; }
        public int Salary{ get; set; }
        public ManagementLevel ManageLvl{ get; set; }
    
        public int CompareTo(Employee employee)
        {
            return this.ManageLvl.CompareTo(employee.ManageLvl);
        }
    }
    void Main()
    {
        List<Employee> employeeList = new List<Employee>();
        
        Employee emp = new Employee();
        emp.Name = "ManyTitles";  emp.Salary = 1234;
        emp.ManageLvl = ManagementLevel.RegionalManager;
        employeeList.Add(emp);
        
        Employee cto = new Employee();
        cto.Name = "CTO";  cto.Salary = 2345;
        cto.ManageLvl = ManagementLevel.CTO;
        employeeList.Add(cto);
    
        Employee ceo = new Employee();
        ceo.Name = "CEO";  ceo.Salary = 123456;
        ceo.ManageLvl = ManagementLevel.CEO;
        employeeList.Add(ceo);
    
        Employee cfo = new Employee();
        cfo.Name = "CFO";  cfo.Salary = 99999;
        cfo.ManageLvl = ManagementLevel.CFO;
        employeeList.Add(cfo);
        
        employeeList.Dump("unordered");
        employeeList.Sort();
        employeeList.Dump("ordered");
        employeeList.Reverse();
        employeeList.Dump("reversed");
    }

    The .Dump method is provided by LINQPad5

    output:

    unordered

    List<Employee> (4 items)
    Name Salary ManageLvl
    ManyTitles 1234 RegionalManager
    CTO 2345 CTO
    CEO 123456 CEO
    CFO 99999 CFO


    ordered

    List<Employee> (4 items)
    Name Salary ManageLvl
    CEO 123456 CEO
    CFO 99999 CFO
    CTO 2345 CTO
    ManyTitles 1234 RegionalManager


    reversed

    List<Employee> (4 items)
    Name Salary ManageLvl
    ManyTitles 1234 RegionalManager
    CTO 2345 CTO
    CFO 99999 CFO
    CEO 123456 CEO

    References:

    https://support.microsoft.com/en-us/kb/320727 "How to use the IComparable and IComparer interfaces in Visual C#"

    http://www.codedigest.com/Articles/CSHARP/84_Sorting_in_Generic_List.aspx "Sorting in C# Generic List(List<T>)"

    it is not working without creating a separate class

    From the first reference above:

    The role of IComparer is to provide additional comparison mechanisms. ...

    Using IComparer is a two-step process.

    First, declare a class that implements IComparer,
    and then implement the Compare method

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Tuesday, January 12, 2016 9:12 AM
  • User1231829591 posted

    Hi, thanks for replying. At first I wasn't clear as to why a new class had to be created to implement IComparer since I was following a tutorial online which did not explain the details on usage.

    Tuesday, January 12, 2016 6:55 PM
  • User303363814 posted

    Alternatively, don't implement IComp* at all just use linq.

    var reversedList = employeeList.OrderByDescending(e => e.ManageLvl);

    Tuesday, January 12, 2016 10:27 PM