Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to turn an IEnumerable into an IOrderedEnumerable without using OrderBy?

Say there is an extension method to order an IQueryable based on several types of Sorting (i.e. sorting by various properties) designated by a SortMethod enum.

public static IOrderedEnumerable<AClass> OrderByX(this IQueryable<AClass> values,     SortMethod? sortMethod) {      IOrderedEnumerable<AClass> queryRes = null;     switch (sortMethod)     {         case SortMethod.Method1:             queryRes = values.OrderBy(a => a.Property1);             break;         case SortMethod.Method2:             queryRes = values.OrderBy(a => a.Property2);             break;         case null:             queryRes = values.OrderBy(a => a.DefaultProperty);             break;         default:             queryRes = values.OrderBy(a => a.DefaultProperty);             break;     }     return queryRes; } 

In the case where sortMethod is null (i.e. where it is specified that I don't care about the order of the values), is there a way to instead of ordering by some default property, to instead just pass the IEnumerator values through as "ordered" without having to perform the actual sort?

I would like the ability to call this extension, and then possibly perform some additional ThenBy orderings.

like image 433
NominSim Avatar asked Jan 18 '13 17:01

NominSim


People also ask

Is LINQ OrderBy stable?

This method performs a stable sort; that is, if the keys of two elements are equal, the order of the elements is preserved.

What is IOrderedEnumerable in c#?

Sorts the elements of a sequence in ascending order by using a specified comparer. OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>) Sorts the elements of a sequence in descending order according to a key. OrderByDescending<TSource,TKey>(IEnumerable<TSource>, Func<TSource,TKey>, IComparer<TKey>)

How does OrderBy work in LINQ?

In the LINQ-OrderBy method, it supports both query and method syntax. Let's see the query syntax with examples. OrderBy sorts the values of a collection in ascending or descending order. It sorts the collection in ascending order by default because ascending keyword is optional here.


2 Answers

All you need to do for the default case is:

queryRes = values.OrderBy(a => 1); 

This will effectively be a noop sort. Because the OrderBy performs a stable sort the original order will be maintained in the event that the selected objects are equal. Note that since this is an IQueryable and not an IEnumerable it's possible for the query provider to not perform a stable sort. In that case, you need to know if it's important that order be maintained, or if it's appropriate to just say "I don't care what order the result is, so long as I can call ThenBy on the result).

Another option, that allows you to avoid the actual sort is to create your own IOrderedEnumerable implementation:

public class NoopOrder<T> : IOrderedEnumerable<T> {     private IQueryable<T> source;     public NoopOrder(IQueryable<T> source)     {         this.source = source;     }      public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)     {         if (descending)         {             return source.OrderByDescending(keySelector, comparer);         }         else         {             return source.OrderBy(keySelector, comparer);         }     }      public IEnumerator<T> GetEnumerator()     {         return source.GetEnumerator();     }      IEnumerator IEnumerable.GetEnumerator()     {         return source.GetEnumerator();     } } 

With that your query can be:

queryRes = new NoopOrder<AClass>(values); 

Note that the consequence of the above class is that if there is a call to ThenBy that ThenBy will effectively be a top level sort. It is in effect turning the subsequent ThenBy into an OrderBy call. (This should not be surprising; ThenBy will call the CreateOrderedEnumerable method, and in there this code is calling OrderBy, basically turning that ThenBy into an OrderBy. From a conceptual sorting point of view, this is a way of saying that "all of the items in this sequence are equal in the eyes of this sort, but if you specify that equal objects should be tiebroken by something else, then do so.

Another way of thinking of a "no op sort" is that it orders the items based in the index of the input sequence. This means that the items are not all "equal", it means that the order input sequence will be the final order of the output sequence, and since each item in the input sequence is always larger than the one before it, adding additional "tiebreaker" comparisons will do nothing, making any subsequent ThenBy calls pointless. If this behavior is desired, it is even easier to implement than the previous one:

public class NoopOrder<T> : IOrderedEnumerable<T> {     private IQueryable<T> source;     public NoopOrder(IQueryable<T> source)     {         this.source = source;     }      public IOrderedEnumerable<T> CreateOrderedEnumerable<TKey>(Func<T, TKey> keySelector, IComparer<TKey> comparer, bool descending)     {         return new NoopOrder<T>(source);     }      public IEnumerator<T> GetEnumerator()     {         return source.GetEnumerator();     }      IEnumerator IEnumerable.GetEnumerator()     {         return source.GetEnumerator();     } } 
like image 163
Servy Avatar answered Sep 20 '22 09:09

Servy


If you return always the same index value you will get an IOrderedEnumerable that preserve the original list order:

case null:      queryRes = values.OrderBy(a => 1);      break; 

Btw I don't think this is a right thing to do. You will get a collection that is supposted to be ordered but actually it is not.

like image 22
Heisenbug Avatar answered Sep 22 '22 09:09

Heisenbug