Using IComparer for sorting

前端 未结 4 641
南方客
南方客 2020-11-28 10:31

I am trying to use an IComparer to sort a list of Points. Here is the IComparer class:

public class CoordinatesBasedComparer : IComparer
{
    p         


        
相关标签:
4条回答
  • 2020-11-28 10:48

    You need to implement the strongly type interface (MSDN).

    public class CoordinatesBasedComparer : IComparer<Point>
    {
        public int Compare(Point a, Point b)
        {
            if ((a.x == b.x) && (a.y == b.y))
                return 0;
            if ((a.x < b.x) || ((a.x == b.x) && (a.y < b.y)))
                return -1;
    
            return 1;
        }
    }
    

    BTW, I think you use too many braces, I believe they should be used only when they contribute to the compiler. This is my version:

    if (a.x == b.x && a.y == b.y)
        return 0;
    if (a.x < b.x || (a.x == b.x && a.y < b.y))
        return -1;
    

    Just like I dislike people using return (0).


    Note that if you target a .Net-3.5+ application you can use LINQ which is easier and even faster with sorting.

    LINQ vesion can be something like:

    var orderedList = Points.OrderBy(point => point.x)
                            .ThenBy(point => point.y)
                            .ToList();
    
    0 讨论(0)
  • 2020-11-28 10:48

    I was getting an InvalidOperation error while adding an object of type MyClass to a SortedList<MyClass>. I was, incorrectly, implementing the IComparer interface. What I needed to implement was IComparable with the method CompareTo(MyClass other), instead of the ICompare.Compare(MyClass x, MyClass y). This is a simplified example:

    SortedList<MyClass> sortedList = new SortedList<MyClass>();
    MyClass a=new MyClass(), b=new MyClass();
    sortedList.Add(a);
    sortedList.Add(b); // Note, sort only happens once second element is added
    

    This fixed it:

    public class MyClass : IComparable<MyClass>
    {
        int IComparable<MyClass>.CompareTo(MyClass other)
        {
            // DoCompareFunction(this, other); and return -1,0,1
        }
    }
    

    This was broken (don't do this if adding to SortedList<MyClass>):

    public class MyClass : IComparer<MyClass>
    {
        int IComparable<MyClass>.Compare(MyClass x, MyClass y)
        {
            // DoCompareFunction(x, y); and return -1,0,1
        }
    }
    

    This was the error:

    Failed to compare two elements in the array.
    at System.Collections.Generic.ArraySortHelper`1.BinarySearch(T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
    at System.Array.BinarySearch[T](T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
    at System.Collections.Generic.SortedList`2.Add(TKey key, TValue value)

    0 讨论(0)
  • 2020-11-28 10:51

    If you're slow like me, the -1 and 1 can be difficult to reason about when using IComparer. The way to think about it is when x should go first, return -1. When y should go first, return 1.

    It can still get confusing if you have lots of fields to sort by. You can use an Enum to make your comparison logic more readable than 1 and -1, then cast the result.

    This example puts objects with the least amount of null fields in the front.

    public class NullishObjectsToTheBackOfTheLine: IComparer<ClassToCompare>
    {
        private enum Xy
        {
            X = -1,
            Both = 0,
            Y = 1
        };
    
        //the IComparer implementation wraps your readable code in an int cast.
        public int Compare(ClassToCompare x, ClassToCompare y)
        {
            return (int) CompareXy(x, y);
        }
    
        private static Xy CompareXy(ClassToCompare x, ClassToCompare y)
        {
            if (x == null && y == null) return Xy.Both;
    
            //put any nulls at the end of the list
            if (x == null) return Xy.Y;
            if (y == null) return Xy.X;
    
            if (x.Country == y.Country && x.ProductId == y.ProductId) return Xy.Both;
    
            //put the least amount of at the front
            if (x.ProductId == null && x.Country == null) return Xy.Y;
            if (y.ProductId == null && y.Country == null) return Xy.X;
    
            //put the country values that are not nulls in front
            if (x.Country != y.Country) return x.Country != null ? Xy.X :  Xy.Y;
    
            //if we got this far, one of these has a null product id and the other doesn't
            return x.ProductId != null ? Xy.X : Xy.Y;
        }
    
    }
    
    public class ClassToCompare
    {
        public string Country { get; set; }
        public string ProductId { get; set; }
    }
    
    0 讨论(0)
  • 2020-11-28 11:06
    public class CoordinatesBasedComparer : IComparer, IComparer<Point>
    {
        public int Compare(Point a, Point b)
        {
            if ((a.x == b.x) && (a.y == b.y))
                return 0;
            if ((a.x < b.x) || ((a.x == b.x) && (a.y < b.y)))
                return -1;
    
            return 1;
        }
        int IComparer.Compare(Object q, Object r)
        {
            return Compare((Point)q, (Point)r);            
        }
    }
    
    0 讨论(0)
提交回复
热议问题