A better way of getting the average salary

Related to my post yesterday in which I tried to show an appealing business sample in F#, David Taylor commented that this:

can't possibly be more appealing than this:

var averageSalary = companyRoll.Average(e=>e.salary);

David is using LINQ of course and his example kills mine by a long shot. I thought a bit about why he did so much better and there are two main reasons:

  1. David is leveraging the framework (LINQ in this case) by using the pre-defined Average() function
  2. Furthermore, he is using a lambda expression (e=>e.salary) to get the salary of every employee

I was really silly at not using this last trick as lambda expressions are *the* core component of functional languages like F#, so I rewrote the function that calculates the average salary using a lambda expression:

I simply stole David's idea and used the lambda function (fun x -> x.salary) to get the salary of an employee, List.map then applies that function to every item on employees and so we get the salaries list in a more compact and efficient way than in my original salaries function. You may also have noticed that I used the betterAverage function:

Which is more compact and readable (for an imperative eye, anyways) than my old average function, the gist of the work (using fold1_left to add up the numbers) is the same as before, but using the if-then-else expression to avoid the division by zero is better than the original match (which I abused, I guess just to show that I can do pattern matching too).

With these new functions betterAverageSalary and betterAverage, you just fill tempted to join them to get something like this:

And so we can get the average salary of a list of employees in one line of code, but without doubt this is too much, because now it's too hard to understand what's going on (this reminds me of the competitions we had at college to see who can possibly solve a problem with the fewer lines of C or Fortran, no matter how uncomprehensible was the code). 

So we could try to combine both definitions in a more controlled way:

Where we define a local function nonEmptySalaryAverage that uses a more local definition (salaries). It is worth noting that these two functions have employees as an implicit parameter by way of the closure mechanism -but I digress. On second thought, I'll stick to my first refinement, among other reasons, because I'll most certainly reuse betterAverage in other places. Thanks to Dave, I'm already a better functional programmer (and yes, Dave, I know what you meant was that it would be better to show an example that couldn't be easily replaced by pre-defined LINQ functions like Average(), more on that in a few nights).

1 Comment

  • I came up with the following lines:

    let averageSalary l =
    let sum,num = List.fold_left (fun (s,n) x -> s+x.salary, n+1) (0.,0) l in
    sum / float num

    The point is to save the number of iterations: map, fold, length each costs an extra round. It returns NaN when the list is empty, which I think is more accurate than returning 0.

Comments have been disabled for this content.