ZetCode

F# Map

last modified September 20, 2023

In this article we show how to work with a Map collection in F#.

A map is is an immutable collection of key/value pairs. The maps are accessed by a key.

F# Map simple example

The following is a simple map example.

main.fsx
let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

printfn "%A" words
printfn "%s" words[1]
printfn "%s" words[4]

We have a map of words. The keys are integers and the values are strings.

let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

The keys are separated from values with a comma character. The pairs are separated with a semicolon.

printfn "%A" words

With the %A format specifier, we pretty-print the map.

printfn "%s" words[1]
printfn "%s" words[4]

We print the elements with index 1 and 4.

λ dotnet fsi main.fsx 
map [(1, "book"); (2, "sky"); (3, "work"); (4, "cloud")]
book
cloud

F# Map size

The Count property returns the number of pairs in the map.

main.fsx
let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

let n = words.Count
printfn $"The map has {n} elements"

The example prints the number of elements int he map.

F# Map iteration

In the next example, we loop over the elements of a Map.

main.fsx
let words = Map [1, "book"; 2, "sky"; 3, "work"; 4, "cloud"]

printfn "%A" words

words |> Map.iter (fun k v -> printfn ($"{k}: {v}"))

for key in words.Keys do
    printfn "%d" key

for value in words.Values do
    printfn "%s" value

for e in words do 
    printfn $"{e.Key}: {e.Value}"

We provide two basic ways of iteration: functional and imperative. The functional uses the Map.iter function, while the imperative uses for loops.

words |> Map.iter (fun k v -> printfn ($"{k}: {v}"))

The Map.iter is the functional way of looping over map elements.

for key in words.Keys do
    printfn "%d" key

We loop over the keys of the map.

for value in words.Values do
    printfn "%s" value

We iterate over the values of the map.

for e in words do 
    printfn $"{e.Key}: {e.Value}"

We iterate over the pairs of the map.

λ dotnet fsi main.fsx 
map [(1, "book"); (2, "sky"); (3, "work"); (4, "cloud")]
1: book
2: sky
3: work
4: cloud
1
2
3
4
book
sky
work
cloud
1: book
2: sky
3: work
4: cloud

F# Map.filter

We can filter map elements with Map.filter.

main.fsx
let words =
    Map [ 1, "book"
          2, "sky"
          3, "work"
          4, "cloud"
          5, "water"
          6, "war" ]

words
|> Map.filter (fun _ v -> v.Contains "w")
|> Map.values
|> Seq.iter (printfn "%s")

In the program, we find out all values which start with 'w'.

words
|> Map.filter (fun _ v -> v.Contains "w")
|> Map.values
|> Seq.iter (printfn "%s")

We pass a predicate lambda to the filter method; it checks if the value contains 'w'. The result is passed to the Map.values to extract all values. Then the values are iterated and printed to the console.

λ dotnet fsi main.fsx 
work
water
war

F# Map Remove

The Remove method returns a new map from which the specified pair is removed.

main.fsx
let words =
    Map [ 1, "book"
          2, "sky"
          3, "work"
          4, "cloud"
          5, "water"
          6, "war" ]

let res = words.Remove 1
printfn "%A" res
printfn "%A" words

In the example, we remove the element with key 1. The original map is not changed.

λ dotnet fsi main.fsx 
map [(2, "sky"); (3, "work"); ... (6, "war")]
map [(1, "book"); (2, "sky"); (3, "work"); ... (6, "war")]

F# Map Add

With Add, we add a new element to the map.

main.fsx
let words =
    Map [ 1, "book"
          2, "sky"
          3, "work"
          4, "cloud"
          5, "water"
          6, "war" ]

let res = Map.add 7 "falcon" words
printfn "%A" res
printfn "%A" words

We add a new pair to the map.

λ dotnet fsi main.fsx 
map [(1, "book"); (2, "sky"); ... (6, "war"); (7, "falcon")]
map [(1, "book"); (2, "sky"); ... (6, "war")]

F# Map.empty

We can create an empty map with Map.empty and add new elements with Add.

main.fsx
type User = {
    Name: string
    Occupation: string
}

let users =
   Map.empty.
      Add(1, {Name="John Doe"; Occupation="gardener"}).
      Add(2, {Name="Roger Roe"; Occupation="driver"}).
      Add(3, {Name="Lucy Smith"; Occupation="teacher"}).
      Add(4, {Name="Tom Jones"; Occupation="programmer"})

users |> Map.iter (fun k v -> printfn $"{k}: {v}")

Utilizing Map.empty, we create a map of users.

λ dotnet fsi main.fsx
1: { Name = "John Doe"
  Occupation = "gardener" }
2: { Name = "Roger Roe"
  Occupation = "driver" }
3: { Name = "Lucy Smith"
  Occupation = "teacher" }
4: { Name = "Tom Jones"
  Occupation = "programmer" }

F# list of maps

In the next example, we define a list of maps.

main.fsx
let fruits1 = Map [ "oranges", 2; "bananas", 3 ]
let fruits2 = Map [ "plums", 4; "kiwis", 5 ]

let all = [ Map[1, fruits1]; Map[2, fruits2] ]

all
|> List.iter (Map.iter (fun k v -> printfn $"{k} {v}"))

printfn "-------------------"

for nested in all do
  for e in nested do
        printfn $"{e.Key} {e.Value}"

We define two maps and insert them into a list. The list is then iterated in declarative and imperative way.

all
|> List.iter (Map.iter (fun k v -> printfn $"{k} {v}"))

We iterate of the list of maps declaratively using List.iter and Map.iter.

for nested in all do
    for e in nested do
          printfn $"{e.Key} {e.Value}"

Imperatively, we loop over the list using two for loops.

λ dotnet fsi main.fsx
1 map [(bananas, 3); (oranges, 2)]
2 map [(kiwis, 5); (plums, 4)]
-------------------
1 map [(bananas, 3); (oranges, 2)]
2 map [(kiwis, 5); (plums, 4)]

In this article we have worked with maps 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.