Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use linq within a loop?

Tags:

c#

linq

Let's start with some source data to query:

int[] someData = { 1, 2 };

After running the following code, things work as I expect: a contains 2 elements which boil down to 1 and 2 pulled from someData.

List<IEnumerable<int>> a = new List<IEnumerable<int>>();
a.Add(someData.Where(n => n == 1));
a.Add(someData.Where(n => n == 2));

But this next code, which does exactly the same thing only in a loop, does not work as expected. When this code completes, b contains 2 elements but they are both identical - pointing to 2. On the 2nd loop, it modifies the first element of b.

List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
    b.Add(someData.Where(n => n == i));
}

Why is this happening and how can I make the loop version behave like the first version?

like image 780
tenfour Avatar asked Dec 07 '22 18:12

tenfour


2 Answers

Jon Skeet has a nice answer here

You need to assign i to a temp variable and use that in the Linq query

List<IEnumerable<int>> b = new List<IEnumerable<int>>();
for (int i = 1; i <= 2; ++i)
{
    int temp = i;
    b.Add(someData.Where(n => n == temp));
}
like image 73
Aducci Avatar answered Dec 10 '22 02:12

Aducci


Your problem is lazy evaluation. You add an Enumerable to b that represents someData.Where(n => n == i). This is evaluated whenever you look into an element of b with the value i has at that time.

You want to manifest the enumerable by calling ToArray() or ToList() on it.

for (int i = 1; i <= 2; ++i)
{
    b.Add(someData.Where(n => n == i).ToArray());
}

Alternatively you can reduce the scope of the captured variable:

for (int i = 1; i <= 2; ++i)
{
    int localI=i;
    b.Add(someData.Where(n => n == localI));
}

Then you still have lazily evaluated enumerables(which shows when you modify someData), but each has a different i.

like image 45
CodesInChaos Avatar answered Dec 10 '22 02:12

CodesInChaos