F# is a functional language, therefore the LINQ functionality in .NET (which is also functional) comes naturally to F#. In this article we will compare LINQ expressions to the equivalent F# expressions for an F# List and show how to use projection, filtering, and other LINQ goodies in F#.
Projection
Projection is the art of mapping one value into another. In LINQ we generally map an entire collection into a new collection based on the demands of a particular function using the Select method.
Let's say we have a sequence of numbers in C# from 0 to 10:
int[] integers = new int[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
We can easily map each one of these numbers to an even number by multiplying each one by 2. Using LINQ this is a fairly straightforward Select call.
var evens = integers.Select(e => e * 2).ToArray();
The result of this call is:
{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20}
How do we do the same thing in F#? A list is defined in F# as follows:
let integers = [0;1;2;3;4;5;6;7;8;9;10]
You can also write the equivalent list declaration in F# as:
let integers= [0..10]
To do the projection in F#, you write a similar expression that you would in LINQ. The only difference is that you "pipe" your list into the list projection function. Here is the equivalent LINQ expression in F# for generating even numbers:
let evens = integers |> List.map(fun(x) -> x * 2)
The map List function in F# is the same as the LINQ Select in C#. The lambda expression is passed the same way as C# , inside the projection call. fun(x) -> x * 2 is equivalent to the anonymous delegate in C#.
Here are the results from the F# code projecting even numbers:
val evens : int list = [0; 2; 4; 6; 8; 10; 12; 14; 16; 18; 20]
Adding Anonymous Classes to the Projection
One nice feature about LINQ is that you can do a projection, and create an anonymous class on the fly to represent your projected item. For example, you could take the integer number list and project it to an instance of an anonymous class containing the number and its product.
var items = integers.Select(e => new { number = e, product = e * 2 }).ToList();
Notice that we didn't have to declare a new class containing those fields, C# generates it for us.
F# has an equivalent mechanism called Object Expressions. You can declare your anonymous class on the fly and then use it in your List projection as follows:
type Pair = {Number : int; Product : int;}
let productPairs = list |> List.map(fun e -> {Number = e; Product = e*2});;
F# just figures out from the type assignment that you are using the anonymous class defined in the type Pair in your lambda expression. Here are the results from the F# interactive window
[{Number = 0; Product = 0;}; {Number = 1; Product = 2;}; {Number = 2; Product = 4;}; {Number = 3; Product = 6;}; {Number = 4; Product = 8;}; {Number = 5; Product = 10;}; {Number = 6; Product = 12;}; {Number = 7; Product = 14;}; {Number = 8; Product = 16;}; {Number = 9; Product = 18;}; {Number = 10; Product = 20;}]
Getting Indexes from the Array
LINQ lets you automatically get the index of your list item in the select if you just pass the additional parameter as the first parameter in your lambda expression.
var odds = integers.Select((index, x) => new { Index = index, Product = x * 3 }).Where(o => o.Product % 2 == 1).ToArray();
The F# List library has equivalent functionality with a function called mapi. Here is how you could do the same thing in F#:
let odds = list |> List.mapi(fun index x -> {Number = index; Product = x * 3});;
I like the way F# broke out the index functionality into a new method, because it adheres closer to the single responsibility principle than LINQ does for this function.
The results of the mapi example are shown below
[{Number = 0; Product = 0;}; {Number = 1; Product = 3;}; {Number = 2; Product = 6;}; {Number = 3; Product = 9;}; {Number = 4; Product = 12;}; {Number = 5; Product = 15;}; {Number = 6; Product = 18;}; {Number = 7; Product = 21;}; {Number = 8; Product = 24;}; {Number = 9; Product = 27;}; {Number = 10; Product = 30;}]
Filtering
Another major use of LINQ is for filtering. LINQ uses a Where clause that takes a predicate to determine how to filter a list. Here is a LINQ expression for pulling out all the odd numbers from the integer list into an array:
var odds = integers.Where(o => o % 2 == 1).ToArray();
This LINQ statement will test each element in the integer list and see if when you divide by 2, the remainder is 1. If the remainder is not 1, the expression is false, and the item is not included in the list.
The equivalent statement in LINQ is almost identical, except for the piping again:
let odds = list |> List.filter(fun o -> o % 2 = 1)
What if I wanted to chain several filters together? That's where the piping comes in handy in F#. Let's say I wanted all odd numbers greater than 5. I could put it all in one expression or I could chain the filters together with the piping:
let odds = list |> List.filter(fun x -> x % 2 = 1) |> List.filter(fun x -> x > 5);;
You could also chain your select statement along with the filter. For example, the following returns a list of odd multiples of 3:
let odds = list |> List.map(fun x -> x * 3) |> List.filter(fun x -> x % 2 = 1);;
In C#, the same statement would be similar, where the chaining is carried out with the dot operator:
var odds = integers.Select(x => x * 3).Where(o => o % 2 == 1).ToArray();
Aggregation
There are multiple aggregate functions in LINQ including Count, Sum, Min, and Max. You can also perform your own custom aggregation using the Aggregate function. What if we wanted to add up all the items in the integer list 0 to 10? We could use the Sum LINQ method, but let's see how to do it wth Aggregate. The Aggregate method keeps a running total and continually feeds it back into the lambda expression action.
var sum = integers.Aggregate((runningSum, nextValue) => runningSum + nextValue);
In F#, you can do the same thing using the fold List function. For a simple sum operator, you can just specify the sum operation inside the expression and say where the sum initial value starts. In our case, the sum starts at zero.
let sum = integers |> List.fold(fun runningSum nextVal -> runningSum + nextVal) 0;;
In F# you can take a shortcut and just specify the + operator as the function. The + operator implies that we are summing two numbers together. Here is the equivalent sum aggregate producing the same results (55).
let sum = integers |> List.fold(+) 0;;
Other LINQ Equivalents in the List library
Below is a table that maps LINQ to the F# List Equivalent. Some are not exactly the same, but they are pretty close.
LINQ |
F# |
Description |
Select |
map |
Projection |
Where |
filter |
Filtering |
Aggregate |
fold |
Aggregation |
First |
Head |
First element in a list |
Last |
Tail |
Last element in the list |
Count |
Length |
Number of Elements |
Skip, Take 1 |
Item |
Get an Item in the list by index |
All |
forall |
Tests all the elements of the list with a predicate |
Use custom extension that implements foreach |
Iter |
Applies the given function to each item in the collection |
First(Predicate) |
tryFind |
Finds first element in the list that satisfies predicate |
Select, First |
tryFindIndex |
Finds the index of first element satisfying the predicate passed |
For a complete list of F# List functions, feel free to navigate to
msdn's reference
Conclusion
F# is a powerful functional language with collection LINQ functionality built directly into its libraries. You can use the List library to do almost everything you could do with LINQ and even some additional functions. F# takes some getting used to if you are a C# programmer because the syntax is very different. Luckily, since C# already has functional aspects to the language, such as delegates and immutable types, many of the F# concepts are easy to grasp for the experienced C# programmer. Have F#un playing around with the language, it's sure to give you a few good ideas!