Code equivalent to the 'let' keyword in chained LINQ extension method calls
C#LinqExtension MethodsLinq to-ObjectsC# Problem Overview
Using the C# compilers query comprehension features, you can write code like:
var names = new string[] { "Dog", "Cat", "Giraffe", "Monkey", "Tortoise" };
var result =
from animalName in names
let nameLength = animalName.Length
where nameLength > 3
orderby nameLength
select animalName;
In the query expression above, the let
keyword allows a value to be passed forward to the where and orderby operations without duplicate calls to animalName.Length
.
What is the equivalent set of LINQ extension method calls that achieves what the "let" keyword does here?
C# Solutions
Solution 1 - C#
Let doesn't have its own operation; it piggy-backs off of Select
. You can see this if you use "reflector" to pull apart an existing dll.
it will be something like:
var result = names
.Select(animalName => new { nameLength = animalName.Length, animalName})
.Where(x=>x.nameLength > 3)
.OrderBy(x=>x.nameLength)
.Select(x=>x.animalName);
Solution 2 - C#
There's a good article here
Essentially let
creates an anonymous tuple. It's equivalent to:
var result = names.Select(
animal => new { animal = animal, nameLength = animal.Length })
.Where(x => x.nameLength > 3)
.OrderBy(y => y.nameLength)
.Select(z => z.animal);
Solution 3 - C#
There is also a .Let extension method in System.Interactive, but its purpose is to introduce a lambda expression to be evaluated 'in-line' in a fluent expression. For instance, consider (in LinqPad, say) the following expression that creates new random numbers every time it's executed:
var seq = EnumerableEx.Generate(
new Random(),
_ => true,
_ => _,
x => x.Next());
To see that new random samples show up every time, consider the following
seq.Zip(seq, Tuple.Create).Take(3).Dump();
which produces pairs in which the left and right are different. To produce pairs in which the left and right are always the same, do something like the following:
seq.Take(3).ToList().Let(xs => xs.Zip(xs, Tuple.Create)).Dump();
If we could invoke lambda expressions directly, we might write
(xs => xs.Zip(xs, Tuple.Create))(seq.Take(3).ToList()).Dump();
But we can't invoke lambda expressions as if they were methods.
Solution 4 - C#
above comment is no more valid
var x = new List<int> { 2, 3, 4, 5, 6 }.AsQueryable();
(from val in x
let val1 = val
let val2 = val + 1
where val2 > val1
select val
).Dump();
produces
System.Collections.Generic.List`1[System.Int32]
.Select(
val =>
new
{
val = val,
val1 = val
}
)
.Select(
temp0 =>
new
{
temp0 = temp0,
val2 = (temp0.val + 1)
}
)
.Where(temp1 => (temp1.val2 > temp1.temp0.val1))
.Select(temp1 => temp1.temp0.val)
so multiple let
are optimized now