Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't IOrderedEnumerable retain order after where filtering

Tags:

c#

linq

I've created a simplification of the issue. I have an ordered IEnumerable, I'm wondering why applying a where filter could unorder the objects

This does not compile while it should have the potential to

IOrderedEnumerable<int> tmp = new List<int>().OrderBy(x => x);
//Error Cannot Implicitly conver IEnumerable<int> To IOrderedEnumerable<int>
tmp = tmp.Where(x => x > 1);

I understand that there would be no gaurenteed execution order if coming from an IQueryable such as using linq to some DB Provider.

However, when dealing with Linq To Object what senario could occur that would unorder your objects, or why wasn't this implemented?

EDIT

I understand how to properly order this that is not the question. My Question is more of a design question. A Where filter on linq to objects should enumerate the give enumerable and apply filtering. So why is that we can only return an IEnumerable instead of an IOrderedEnumerable?

EDIT

To Clarify the senario in when this would be userful. I'm building Queries based on conditions in my code, I want to reuse as much code as possible. I have a function that is returning an OrderedEnumerable, however after applying the additional where I would have to reorder this even though it would be in its original ordered state

like image 595
johnny 5 Avatar asked Jun 22 '16 16:06

johnny 5


2 Answers

Rene's answer is correct, but could use some additional explanation.

IOrderedEnumerable<T> does not mean "this is a sequence that is ordered". It means "this is a sequence that has had an ordering operation applied to it and you may now follow that up with a ThenBy to impose additional ordering requirements."

The result of Where does not allow you to follow it up with ThenBy, and therefore you may not use it in a context where an IOrderedEnumerable<T> is required.

Make sense?

But of course, as others have said, you almost always want to do the filtering first and then the ordering. That way you are not spending time putting items into order that you are just going to throw away.

There are of course times when you do have to order and then filter; for example, the query "songs in the top ten that were sung by a woman" and the query "the top ten songs that were sung by a woman" are potentially very different! The first one is sort the songs -> take the top ten -> apply the filter. The second is apply the filter -> sort the songs -> take the top ten.

like image 67
Eric Lippert Avatar answered Oct 04 '22 07:10

Eric Lippert


The signature of Where() is this:

public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)

So this method takes an IEnumerable<int> as first argument. The IOrderedEnumerable<int> returned from OrderBy implements IEnumerable<int> so this is no problem.

But as you can see, Where returns an IEnumerable<int> and not an IOrderedEnumerable<int>. And this cannot be casted into one another.

Anyway, the object in that sequence will still have the same order. So you could just do it like this

IEnumerable<int> tmp = new List<int>().OrderBy(x => x).Where(x => x > 1);

and get the sequence you expected.

But of course you should (for performance reasons) filter your objects first and sort them afterwards when there are fewer objects to sort:

IOrderedEnumerable<int> tmp = new List<int>().Where(x => x > 1).OrderBy(x => x);
like image 27
René Vogt Avatar answered Oct 04 '22 06:10

René Vogt