Search code examples
c#sortingicomparer

Implementing IComparer combining multiple Linq OrderBy's


My problem is that I always want to order a collection of objects in a certain fashion.

For example:

class foo{
public string name {get;set;}
public DateTime date {get;set;}
public int counter {get;set;}
}

...

IEnumerable<foo> dosomething(foo[] bar){ 
return bar.OrderBy(a=>a.name).ThenBy(a=>a.date).ThenBy(a=>a.counter);
}

The issue I have is its quite longwinded tacking-on the sort order all the time. A neat solution appears to just create a class that implements IComparer<foo>, meaning I can do:

IEnumerable<foo> dosomething(foo[] bar){ 
return bar.OrderBy(a=>a, new fooIComparer())
}

.

The problem is, the order method this implements is as follows

...

public int Compare(foo x, foo y){ }

Meaning it compares on a very granular basis.

The currently implementation (which will probably work, although im writing pseudocode)

public int Compare(foo x, foo y){
if (x==y)
  return 0;
var order = new []{x,y}.OrderBy(a=>a.name).ThenBy(a=>a.date).ThenBy(a=>a.counter);
  return (order[0] == x) ? -1 : -1;//if x is first in array it is less than y, else it is greater
}

This is not exactly efficient, can another offer a neater solution? Ideally without a Compare(x,y) method altogether?


Solution

  • You have to implement IComparable<foo> and compare all properties:

    class foo: IComparable<foo>, IComparer<foo>
    {
        public string name { get; set; }
        public DateTime date { get; set; }
        public int counter { get; set; }
    
        public int Compare(foo x, foo y)
        {
            if (x == null || y == null) return int.MinValue;
            if (x.name != y.name)
                return StringComparer.CurrentCulture.Compare(x.name, y.name);
            else if (x.date != y.date)
                return x.date.CompareTo(y.date);
            else if (x.counter != y.counter)
                return x.counter.CompareTo(y.counter);
            else
                return 0;
        }
    
        public int CompareTo(foo other)
        {
            return Compare(this, other);
        }
    }
    

    Then you can use OrderBy in this way:

    var ordered = foos.OrderBy(f => f).ToList();