Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use the IComparable interface?

I need a basic example of how to use the IComparable interface so that I can sort in ascending or descending order and by different fields of the object type I'm sorting.

like image 927
Spencer Ruport Avatar asked Jan 12 '09 13:01

Spencer Ruport


People also ask

How do you implement IComparable interface?

It requires that implementing types define a single method, CompareTo(Object), that indicates whether the position of the current instance in the sort order is before, after, or the same as a second object of the same type. The instance's IComparable implementation is called automatically by methods such as Array.

What is the IComparable interface in C#?

C# IComparable interface The IComparable interface defines a generalized type-specific comparison method that a value type or class implements to order or sort its instances. The IComparable is implemented by types whose values can be ordered or sorted. The interface requires the CompareTo method to be implemented.

Why do we use IComparable in C#?

Use the IComparable Interface in C# to sort elements. It is also used to compare the current instance with another object of same type. It provides you with a method of comparing two objects of a particular type. Remember, while implementing the IComparable interface, CompareTo() method should also be implemented.

How does IComparer work in C#?

The IComparer. Compare method requires a tertiary comparison. 1, 0, or -1 is returned depending on whether one value is greater than, equal to, or less than the other. The sort order (ascending or descending) can be changed by switching the logical operators in this method.


3 Answers

Well, since you are using List<T> it would be a lot simpler to just use a Comparison<T>, for example:

List<Foo> data = ...
// sort by name descending
data.Sort((x,y) => -x.Name.CompareTo(y.Name));

Of course, with LINQ you could just use:

var ordered = data.OrderByDescending(x=>x.Name);

But you can re-introduce this in List<T> (for in-place re-ordering) quite easily; Here's an example that allows Sort on List<T> with lambda syntax:

using System;
using System.Collections.Generic;  

class Foo { // formatted for vertical space
    public string Bar{get;set;}
}
static class Program {
    static void Main() {
        List<Foo> data = new List<Foo> {
            new Foo {Bar = "abc"}, new Foo {Bar = "jkl"},
            new Foo {Bar = "def"}, new Foo {Bar = "ghi"}
        };
        data.SortDescending(x => x.Bar);
        foreach (var row in data) {
            Console.WriteLine(row.Bar);
        }
    }

    static void Sort<TSource, TValue>(this List<TSource> source,
            Func<TSource, TValue> selector) {
        var comparer = Comparer<TValue>.Default;
        source.Sort((x,y)=>comparer.Compare(selector(x),selector(y)));
    }
    static void SortDescending<TSource, TValue>(this List<TSource> source,
            Func<TSource, TValue> selector) {
        var comparer = Comparer<TValue>.Default;
        source.Sort((x,y)=>comparer.Compare(selector(y),selector(x)));
    }
}
like image 193
Marc Gravell Avatar answered Oct 24 '22 00:10

Marc Gravell


Here's a simple example:

public class SortableItem : IComparable<SortableItem>
{
    public int someNumber;

    #region IComparable<SortableItem> Members

    public int CompareTo(SortableItem other)
    {
        int ret = -1;
        if (someNumber < other.someNumber)
            ret = -1;
        else if (someNumber > other.someNumber)
            ret = 1;
        else if (someNumber == other.someNumber)
            ret = 0;
        return ret;
    }

    #endregion
}

"That's great, but what if I want to be able to control the sort order, or sort by another field?"

Simple. All we need to do is add few more fields to the object. First we'll add a string for a different sort type and then we'll add a boolean to denote whether we're sorting in descending or ascending order and then add a field which determines which field we want to search by.

public class SortableItem : IComparable<SortableItem>
{
    public enum SortFieldType { SortNumber, SortString }

    public int someNumber = -1;
    public string someString = "";
    public bool descending = true;    
    public SortFieldType sortField = SortableItem.SortFieldType.SortNumber;        

    #region IComparable<SortableItem> Members

    public int CompareTo(SortableItem other)
    {
        int ret = -1;
        if(sortField == SortableItem.SortFieldType.SortString)
        {
            // A lot of other objects implement IComparable as well.
            // Take advantage of this.
            ret = someString.CompareTo(other.someString);
        }
        else
        {
            if (someNumber < other.someNumber)
                ret = -1;
            else if (someNumber > other.someNumber)
                ret = 1;
            else if (someNumber == other.someNumber)
                ret = 0;
        }
        // A quick way to switch sort order:
        // -1 becomes 1, 1 becomes -1, 0 stays the same.
        if(!descending) ret = ret * -1; 

        return ret;
    }

    #endregion

    public override string ToString()
    {
       if(sortField == SortableItem.SortFieldType.SortString)
          return someString;
       else
          return someNumber.ToString();
    }
}

"Show me how!"

Well since you asked so nicely.

static class Program
{
    static void Main()
    {

        List<SortableItem> items = new List<SortableItem>();
        SortableItem temp = new SortableItem();
        temp.someString = "Hello";
        temp.someNumber = 1;
        items.Add(temp);
        temp = new SortableItem();
        temp.someString = "World";
        temp.someNumber = 2;
        items.Add(temp);
        SortByString(items);
        Output(items);
        SortAscending(items);
        Output(items);
        SortByNumber(items);
        Output(items);
        SortDescending(items);
        Output(items);
        Console.ReadKey();
    }

    public static void SortDescending(List<SortableItem> items)
    {
        foreach (SortableItem item in items)
            item.descending = true;
    }
    public static void SortAscending(List<SortableItem> items)
    {
        foreach (SortableItem item in items)
            item.descending = false;
    }
    public static void SortByNumber(List<SortableItem> items)
    {
        foreach (SortableItem item in items)
            item.sortField = SortableItem.SortFieldType.SortNumber;
    }
    public static void SortByString(List<SortableItem> items)
    {
        foreach (SortableItem item in items)
            item.sortField = SortableItem.SortFieldType.SortString;
    }
    public static void Output(List<SortableItem> items)
    {
        items.Sort();
        for (int i = 0; i < items.Count; i++)
            Console.WriteLine("Item " + i + ": " + items[i].ToString());
    }
}
like image 36
Spencer Ruport Avatar answered Oct 24 '22 00:10

Spencer Ruport


If you want dynamic sort, you can use LINQ

var itemsOrderedByNumber = ( from item in GetClasses() orderby item.Number select item ).ToList();
var itemsOrderedByText = ( from item in GetClasses() orderby item.Text select item ).ToList();
var itemsOrderedByDate = ( from item in GetClasses() orderby item.Date select item ).ToList();

or "Sort" method of List class:

List<Class1> itemsOrderedByNumber2 = new List<Class1>( GetClasses() );
itemsOrderedByNumber2.Sort( ( a, b ) => Comparer<int>.Default.Compare( a.Number, b.Number ) );

List<Class1> itemsOrderedByText2 = new List<Class1>( GetClasses() );
itemsOrderedByText2.Sort( ( a, b ) => Comparer<string>.Default.Compare( a.Text, b.Text ) );

List<Class1> itemsOrderedByDate2 = new List<Class1>( GetClasses() );
itemsOrderedByDate2.Sort( ( a, b ) => Comparer<DateTime>.Default.Compare( a.Date, b.Date ) );
like image 42
TcKs Avatar answered Oct 24 '22 01:10

TcKs