ZetCode

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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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.

main.fsx
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#.

Author

My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.