Implementation of IComparer integrating several Linq OrderBy's

My problem is that I always want to arrange the collection of objects in a certain way.

For instance:

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 question I have is a rather lengthy binding to the sort order all the time. The optimal solution seems to just create a class that implements IComparer<foo> , which I can do:

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

.

The problem is that the implemented order method is as follows

...

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

This means that it is compared on a very competent basis.

The current implementation (which is likely to work though 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 entirely effective, can another suggest a faster solution? Ideally, without the Compare (x, y) method?

+4
source share
4 answers

You need to implement IComparable<foo> and compare all the 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 as follows:

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

Option 1 - Comparison

As you order under several conditions, you will check them individually in each case; for example, if x.name and y.name are equal, you can check x.date and y.date , etc.

 public class FooComparer : IComparer<Foo> { public int Compare(Foo x, Foo y) { // nasty null checks! if (x == null || y == null) { return x == y ? 0 : x == null ? -1 : 1; } // if the names are different, compare by name if (!string.Equals(x.Name, y.Name)) { return string.Compare(x.Name, y.Name); } // if the dates are different, compare by date if (!DateTime.Equals(x.Date, y.Date)) { return DateTime.Compare(x.Date, y.Date); } // finally compare by the counter return x.Counter.CompareTo(y.Counter); } } 

Option 2 - Extension Method

An alternative, less attractive approach might be an extension method. Unfortunately, since the TKey for each ThenBy may be different, we lose the power of generics, but in this case we can safely replace it with the object type.

 public static IOrderedEnumerable<T> OrderByThen<T>(this IEnumerable<T> source, Func<T, object> selector, params Func<T, object>[] thenBySelectors) { IOrderedEnumerable<T> ordered = source.OrderBy(selector); foreach (Func<T, object> thenBy in thenBySelectors) { ordered = ordered.ThenBy(thenBy); } return ordered; } 
+3
source

What happened to the extension method?

+1
source

Why don't you just compare your values:

 int Compare(foo x, foo y) { if (x== null && y == null) return 0; else if (x == null) return -1; else if (y == null) return 1; var nameComparision = string.Compare(x.name,y.name); if (nameComparision != 0) return nameComparision; var dateComparision = x.date.CompareTo(y.date); if (dateComparision != 0) return dateComparision; var counterComparision = x.counter.CompareTo(y.counter); return counterComparision; } 
+1
source

All Articles