In C# LINQ we can use a temporary variable with let
clause:
var users2 = from user in users
let total = users.Sum(u => u.Salary)
let avg = total / n
where user.Salary > avg
select user;
Is it possible to do the same in F# in one go?
let avg = users |> List.averageBy (fun user -> float user.Salary)
let users2 = users |> List.filter (fun user -> user.Salary > int avg)
I was only able to do it in two different operations.
CodePudding user response:
First of all, I think your F# solution is perfectly reasonable and I do not think it needs any improvement. Moreover, you also want to calculate avg
upfront just once - the C# code most likely recomputes this for each user that it looks into (so your C# code would be better if it calculated avg
before the query!)
The C# query comprehension is roughly equivalent to the following F# sequence expression:
let users2 =
[ for u in users do
let avg = users |> List.averageBy (fun user -> float user.Salary)
if u.Salary > int avg then yield u ]
This calculates avg
inside the for
loop (or inside the from
clause) and so this is re-calculated for each user that you are iterating over. You could improve this by moving avg
before for
:
let users2 =
[ let avg = users |> List.averageBy (fun user -> float user.Salary)
for u in users do
if u.Salary > int avg then yield u ]
This does all the processing in a single comprehension and does not recompute avg
, so I think this is as good as it gets. Whether you prefer this or using higher-order functions explicitly is a personal choice - I think your version is probably simpler than this one.