Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ Skip still enumerates skipped items

Tags:

c#

.net

linq

In the following test:

int[] data = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Func<int, int> boom = x => { Console.WriteLine(x); return x; };
var res = data.Select(boom).Skip(3).Take(4).ToList();
Console.WriteLine();
res.Select(boom).ToList();

The result is:

1
2
3
4
5
6
7

4
5
6
7

Essentially, I observed that in this example, Skip() and Take() work well, Skip() is not as lazy as Take(). It seems that Skip() still enumerates the items skipped, even though it does not return them.

The same applies if I do Take() first. My best guess is that it needs to enumerate at least the first skip or take, in order to see where to go with the next one.

Why does it do this?

like image 500
ericosg Avatar asked Dec 03 '15 04:12

ericosg


5 Answers

Skip() and Take() both operate on IEnumerable<>.

IEnumerable<> does not support skipping ahead -- it can only give you one item at a time. With this in mind, you can think of the Skip() more as a filter -- it still touches all the items in the source sequence, but it filters out however many you tell it to. And importantly, it filters them out from getting to whatever is next, not for whatever is in front of it.

So, by doing this:

data.Select(boom).Skip(3)

You are performing boom() on each item before they get to the Skip() filter.

If you instead changed it to this, it would filter prior to the Select and you would call boom() on only the remaining items:

data.Skip(3).Take(4).Select(boom)
like image 169
Cory Nelson Avatar answered Nov 18 '22 11:11

Cory Nelson


If you decompile Enumerable, you will see the following implementation of Skip:

while (count > 0 && e.MoveNext())
  --count;

and the following implementation of Take:

foreach (TSource source1 in source)
{
  yield return source1;
  if (--count == 0) break;
}

So, both of these LINQ methods actually enumerate through these items. The difference is whether an enumerated item will be placed in the resulting collection or not. That's how IEnumerable does work.

like image 40
Yeldar Kurmangaliyev Avatar answered Nov 18 '22 09:11

Yeldar Kurmangaliyev


They both iterate the collection. And then turnout with the final collection. Its just like a simple for loop having a if else condition in it.

Moreover you're selecting it first using boom, it prints the item of collection.

By this example you can not tell if skip() or take() iterates the whole collection or not but the matter of fact, they do.

like image 2
Shaharyar Avatar answered Nov 18 '22 09:11

Shaharyar


assuming i have understood your question correctly.

the boom func executes before the skip operation.

try to select data after skip and take , you will get the exact.

like image 1
karthikkumar subramaniam Avatar answered Nov 18 '22 10:11

karthikkumar subramaniam


This behaviour maybe will be changed in the future, as can be seen here in this discussion about a Pull Request that optimizes the behaviour of the Skip method for sources that implement the IList interface.

like image 1
T_D Avatar answered Nov 18 '22 11:11

T_D