Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to force my lambda expressions to evaluate early? Fix lambda expression weirdness?

Tags:

c#

.net

lambda

linq

I have written the following C# code:

_locationsByRegion = new Dictionary<string, IEnumerable<string>>();
foreach (string regionId in regionIds)
{
    IEnumerable<string> locationIds = Locations
        .Where(location => location.regionId.ToUpper() == regionId.ToUpper())
        .Select(location => location.LocationId); //If I cast to an array here, it works.
    _locationsByRegion.Add(regionId, LocationIdsIds);
}

This code is meant to create a a dictionary with my "region ids" as keys and lists of "location ids" as values.

However, what actually happens is that I get a dictionary with the "region ids" as keys, but the value for each key is identical: it is the list of locations for the last region id in regionIds!

It looks like this is a product of how lambda expressions are evaluated. I can get the correct result by casting the list of location ids to an array, but this feels like a kludge.

What is a good practice for handling this situation?

like image 898
Vivian River Avatar asked Jul 22 '11 18:07

Vivian River


People also ask

What are lambda expressions in Java 8?

Lambda Expressions are anonymous functions. These functions do not need a name or a class to be used. Lambda expressions are added in Java 8. Lambda expressions basically express instances of functional interfaces An interface with a single abstract method is called a functional interface. One example is java.lang.Runnable.

How to store a lambda expression in a variable?

The lambda expression should have the same number of parameters and the same return type as that method. Java has many of these kinds of interfaces built in, such as the Consumer interface (found in the java.util package) used by lists. Use Java's Consumer interface to store a lambda expression in a variable:

Do we need to specify return type in lambda expression?

Generally return-type in lambda expression are evaluated by compiler itself and we don’t need to specify that explicitly and -> return-type part can be ignored but in some complex case as in conditional statement, compiler can’t make out the return type and we need to specify that.

How do lambda expressions have more power than ordinary functions?

A lambda expression can have more power than an ordinary function by having access to variables from the enclosing scope. We can capture external variables from enclosing scope by three ways :


3 Answers

You're using LINQ. You need to perform an eager operation to make it perform the .Select. ToList() is a good operator to do that. List is generic it can be assigned to IEnumberable directly.

In the case where you're using LINQ it does lazy evaluation by default. ToList/eager operations force the select to occur. Before you use one of these operators the action is not performed. It is like executing SQL in ADO.NET kind of. If you have the statement "Select * from users" that doesn't actually perform the query until you do extra stuff. The ToList makes the select execute.

like image 63
m4tt1mus Avatar answered Oct 23 '22 04:10

m4tt1mus


Your closing over the variable, not the value.

Make a local copy of the variable so you capture the current value from the foreach loop instead:

_locationsByRegion = new Dictionary<string, IEnumerable<string>>();
foreach (string regionId in regionIds)
{
    var regionToUpper = regionId.ToUpper();
    IEnumerable<string> locationIds = Locations
        .Where(location => location.regionId.ToUpper() == regionToUpper)
        .Select(location => location.LocationId); //If I cast to an array here, it works.
    _locationsByRegion.Add(regionId, LocationIdsIds);
}

Then read this:

http://msdn.microsoft.com/en-us/vcsharp/hh264182

edit - Forcing a eager evaluation would also work as others have suggested, but most of the time eager evaluations end up being much slower.

like image 20
asawyer Avatar answered Oct 23 '22 05:10

asawyer


Call ToList() or ToArray() after the Select(...). Thus entire collection will be evaluated right there.

like image 4
Ivan Danilov Avatar answered Oct 23 '22 04:10

Ivan Danilov