Home > Enterprise >  F# using a temporary variable in pipe operator aka C# LINQ let
F# using a temporary variable in pipe operator aka C# LINQ let

Time:09-17

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.

  • Related