F# List
last modified September 26, 2023
In this article we show how to work with a List collection in F#.
A list is an ordered, immutable series of elements of the same type.
let vals = [ 1; 2; 3; 4; 5 ]
A list can be created using a list literal. A list literal consists of elements separated with semicolons inside a pair of square brackets.
let vals = [ 1 2 3 4 5 ]
In an alternative syntax, the semicolons are optional.
F# List simple example
The following is a simple list example.
let vals = [ 1; 2; 3; 4; 5; 6 ] printfn "%A" vals printfn "%d" vals.Head printfn "%d" vals.Length printfn "%A" vals.Tail
We have a list of integers. We print the contents of the list, its head, size, and tail.
let vals = [ 1; 2; 3; 4; 5; 6 ]
We define a list of integers with an array literal.
printfn "%A" vals
With the %A
format specifier, we pretty-print the list.
printfn "%d" vals.Head
With the Head
property, we print the first element of the list.
printfn "%d" vals.Length
We get the size of a list with Length
.
printfn "%A" vals.Tail
With Tail
, we get all but first elements of the list.
λ dotnet fsi main.fsx [1; 2; 3; 4; 5; 6] 1 6 [2; 3; 4; 5; 6]
F# List iteration
In the next example, we loop over the elements of a list.
let vals = [ 1; 2; 3; 4; 5; 6 ] vals |> List.iter (printfn "%d") printfn "------------------------" for e in vals do printfn "%d" e
We provide two basic ways of iteration.
vals |> List.iter (printfn "%d")
The List.iter
is the functional way of looping over list elements.
for e in vals do printfn "%d" e
The classic, imperative way is via a for loop.
λ dotnet fsi main.fsx 1 2 3 4 5 6 ------------------------ 1 2 3 4 5 6
F# List indexes
List elements are accessed through their indexes.
let words = ["pen"; "cup"; "dog"; "person"; "cement"; "coal"; "spectacles"; "cup"; "bread"] let w1 = List.item 1 words printfn "%s" w1 let w2 = words[0] printfn "%s" w2 let i1 = List.findIndex(fun e -> e = "cup") words printfn $"The first index of cup is {i1}" let i2 = List.findIndexBack(fun e -> e = "cup") words printfn $"The last index of cup is {i2}"
The program contains List indexing operations.
let w1 = List.item 1 words
We get the second item of the list with List.item
.
let w2 = words[0]
We can also use the classic C style syntax.
let i1 = List.findIndex(fun e -> e = "cup") words
Fith List.findIndex
, we find the first element that satisfies the
given predicate function.
let i2 = List.findIndexBack(fun e -> e = "cup") words
Fith List.findIndexBack
, we find the last element that satisfies
the given predicate function.
λ dotnet fsi main.fsx cup pen The first index of cup is 1 The last index of cup is 7
F# List.map
The List.map
function applies the given function to each of the
elements of the collection.
let vals = [1..10] let res = List.map(fun e -> e * 2) vals printfn "%A" res
We apply a map function on a list of integers.
let vals = [ 1 .. 10]
We define a list with a range operator.
let res = List.map(fun e -> e * 2) vals
Each of the elements of the list is multiplied by 2. The result is assigned
to the res
variable.
λ dotnet fsi main.fsx [2; 4; 6; 8; 10; 12; 14; 16; 18; 20]
F# List.filter
We can filter list elements with List.filter
.
let vals = [-3; -2; 0; 1; -5; 7; 9] let words = ["sky"; "war"; "rock"; "ocean"; "cloud"; "water"] let pos = List.filter(fun e -> e > 0) vals printfn "%A" pos let res = List.filter(fun (e:string) -> e.StartsWith("w")) words printfn "%A" res
In the program, we find out all positive numbers from a list of integers and all strings which start with 'w' in a list of words.
let pos = List.filter(fun e -> e > 0) vals
The List.filter
function takes a predicate function. All elements
must satisfy the given predicate.
let res = List.filter(fun (e:string) -> e.StartsWith("w")) words
Sometines, it is necessary to help the compiler with an explicity type definition.
λ dotnet fsi main.fsx [1; 7; 9] ["war"; "water"]
List.zip
The List.zip
function combines the two lists into a list of pairs.
The two lists must have equal lengths.
let words = ["sky"; "cup"; "rock"; "pen"; "pearl"; "cloud"] let n = words.Length let idxs = [1..n] let data = List.zip idxs words printfn "%A" data printfn "-----------------" let m = data |> Map.ofList m |> Map.iter (fun k v -> printfn $"{k}: {v}");
We combine a list of strings with a list of integers. Then we convert the list into a map.
λ dotnet fsi main.fsx [(1, "sky"); (2, "cup"); (3, "rock"); (4, "pen"); (5, "pearl"); (6, "cloud")] ----------------- 1: sky 2: cup 3: rock 4: pen 5: pearl 6: cloud
F# merging lists
With the @
operator, we can merge two lists.
let a = [1; 2; 3; 4] let b = [4; 4; 5; 6] let merged = a @ b |> List.distinct printfn "%A" merged let merged2 = a @ b printfn "%A" merged2
The program merges two lists.
let merged = a @ b |> List.distinct
We merge two lists and pass the values to List.distinct
, which
removes duplicates.
let merged2 = a @ b
We merge two lists; we have all values, including duplicates.
λ dotnet fsi main.fsx [1; 2; 3; 4; 5; 6] [1; 2; 3; 4; 4; 4; 5; 6]
F# sort List of integers
In the next example, we sort integers.
let nums = [ -1; 6; -2; 3; 0; -4; 5; 1; 2 ] nums |> List.sort |> printfn "%A" nums |> List.sortDescending |> printfn "%A" nums |> List.sortBy (abs) |> printfn "%A" nums |> List.sortByDescending (abs) |> printfn "%A"
We have a list of integers. We sort them with List.sort
and
List.sortDescending
.
nums |> List.sortBy (abs) |> printfn "%A" nums |> List.sortByDescending (abs) |> printfn "%A"
With the help of the abs
, we sort integers regarless of their sign.
λ dotnet fsi main.fsx [-4; -2; -1; 0; 1; 2; 3; 5; 6] [6; 5; 3; 2; 1; 0; -1; -2; -4] [0; -1; 1; -2; 2; 3; -4; 5; 6] [6; 5; -4; 3; -2; 2; -1; 1; 0]
F# sort List of records
In the next example, we sort a list of records.
type User = { Name: string Occupation: string Salary: int } let users = [ { Name = "John Doe" Occupation = "gardener" Salary = 1280 } { Name = "Roger Roe" Occupation = "driver" Salary = 860 } { Name = "Tom Brown" Occupation = "shopkeeper" Salary = 990 } ] users |> List.sortBy (fun u -> u.Salary) |> List.iter (fun u -> printfn "%A" u) printfn "--------------------------------" users |> List.sortByDescending (fun u -> u.Occupation) |> List.iter (fun u -> printfn "%A" u)
The program contains a list of User
records. We sort the users
by their salaries and occupation.
users |> List.sortBy (fun u -> u.Salary) |> List.iter (fun u -> printfn "%A" u)
The users are sorted by salaries in ascending order with
List.sortBy
.
users |> List.sortByDescending (fun u -> u.Occupation) |> List.iter (fun u -> printfn "%A" u)
Here, the users are sorted by their occupation in descending order with
sortByDescending
.
λ dotnet fsi main.fsx { Name = "Roger Roe" Occupation = "driver" Salary = 860 } { Name = "Tom Brown" Occupation = "shopkeeper" Salary = 990 } { Name = "John Doe" Occupation = "gardener" Salary = 1280 } -------------------------------- { Name = "Tom Brown" Occupation = "shopkeeper" Salary = 990 } { Name = "John Doe" Occupation = "gardener" Salary = 1280 } { Name = "Roger Roe" Occupation = "driver" Salary = 860 }
F# List comprehension
List comprehension is a powerful syntax to generate lists. List comprehensions provide a concise way to create lists.
In F#, we can create list comprehensions with ranges and generators.
let vals = [ -1; 0; 2; -2; 1; 3; 4; -6 ] let pos = [ for e in vals do if e > 0 then yield e ] printfn "%A" pos printfn "---------------------------------" [ for e in 1 .. 100 -> e * e ] |> printfn "%A" printfn "---------------------------------" [ for a in 1 .. 100 do if a % 3 = 0 && a % 5 = 0 then yield a] |> printfn "%A" printfn "---------------------------------" let vals3 = [ for x in 1 .. 3 do for y in 1 .. 10 -> x, y ] printfn "%A" vals3
In F#, list comprehensions use for loops, if conditions and the yield keyword.
let vals = [ -1; 0; 2; -2; 1; 3; 4; -6 ] let pos = [ for e in vals do if e > 0 then yield e ]
We have a list of values. A new list is constructed with a list comprehension. It contains only positive values.
[ for e in 1 .. 100 -> e * e ] |> printfn "%A"
We can use ranges in list comprehensions.
[ for a in 1 .. 100 do if a % 3 = 0 && a % 5 = 0 then yield a] |> printfn "%A"
Here we use two if conditions.
let vals3 = [ for x in 1 .. 3 do for y in 1 .. 10 -> x, y ]
Also, it is possible to use two for loops.
λ dotnet fsi main.fsx [2; 1; 3; 4] --------------------------------- [1; 4; 9; 16; 25; 36; 49; 64; 81; 100; 121; 144; 169; 196; 225; 256; 289; 324; 361; 400; 441; 484; 529; 576; 625; 676; 729; 784; 841; 900; 961; 1024; 1089; 1156; 1225; 1296; 1369; 1444; 1521; 1600; 1681; 1764; 1849; 1936; 2025; 2116; 2209; 2304; 2401; 2500; 2601; 2704; 2809; 2916; 3025; 3136; 3249; 3364; 3481; 3600; 3721; 3844; 3969; 4096; 4225; 4356; 4489; 4624; 4761; 4900; 5041; 5184; 5329; 5476; 5625; 5776; 5929; 6084; 6241; 6400; 6561; 6724; 6889; 7056; 7225; 7396; 7569; 7744; 7921; 8100; 8281; 8464; 8649; 8836; 9025; 9216; 9409; 9604; 9801; 10000] --------------------------------- [15; 30; 45; 60; 75; 90] --------------------------------- [(1, 1); (1, 2); (1, 3); (1, 4); (1, 5); (1, 6); (1, 7); (1, 8); (1, 9); (1, 10); (2, 1); (2, 2); (2, 3); (2, 4); (2, 5); (2, 6); (2, 7); (2, 8); (2, 9); (2, 10); (3, 1); (3, 2); (3, 3); (3, 4); (3, 5); (3, 6); (3, 7); (3, 8); (3, 9); (3, 10)]
In this article we have worked with lists in F#.