Today we learn Map, an immutable collection that stores key-value pairs
Map is immutable, it cannot be changed after created. Adding or removing elements return a new Map instead of modifying the existing one
Create Map
- Explicitly specifying elements
let notEmptyMap = Map [ (1, "a"); (2, "b") ]
printfn "%A" notEmptyMap // map [(1, a); (2, b)]
- Map.ofList
let map =
Map.ofList [
(3, "three")
(1, "one")
(2, "two")
]
printfn "map: %A" map // map [(1, one); (2, two); (3, three)]
we can see the key is sorted from 3,1,2 to 1,2,3
what if the key is dulpicate
let map =
Map.ofList [
(3, "three")
(1, "one")
(2, "two") // first key 2
(2, "overwrite") // second key 2
]
printfn "%A" map // map [(1, one); (2, overwrite); (3, three)]
the value of the same key 2 is the last one, it overwrite the first one
- Map.empty
create an empty map with int keys and string values
let emptyMap = Map.empty<int, string>
printfn "%A" emptyMap // map []
Access element
- ContainsKey
Tests if an element is in the domain of the map
let sample = Map [ (1, "a"); (2, "b") ]
printfn "sample contain key 1: %b" (sample.ContainsKey 1) // sample contain key 1: true
printfn "sample contain key 3: %b" (sample.ContainsKey 3) // sample contain key 3: false
- map.[key]
access by key in the domain will return the value, raise Exception if key not exists
let sample = Map [ (1, "a"); (2, "b") ]
printfn "access by key 1: %s" sample.[1] // access by key 1: a
printfn "access by key 3: %s" sample.[3] // Unhandled exception. System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary
if you use browser environment, you may not see this Exception
- TryFind
Lookup an element in the map, returning a Some
value if the element is in the domain of the map and None
if not
let sample = Map [ (1, "a"); (2, "b") ]
printfn "TryFind key 1: %A" (sample.TryFind 1) // evaluates to Some "a"
printfn "TryFind key 3: %A" (sample.TryFind 3) // evaluates to None
- TryGetValue
Lookup an element in the map, assigning to value
if the element is in the domain of the map and returning false
if not
let sample = Map [ (1, "a"); (2, "b") ]
printfn "access by key 1: %A" (sample.TryGetValue 1) // evaluates to (true, "a")
printfn "access by key 3: %A" (sample.TryGetValue 3) // evaluates to (false, null)
TryGetValue
is added in F# 6.0
Conclusion: TryFind
TryGetValueThe
provide a safer way to access elements, and option
type returned by TryFind
TryGetValueThe
fits naturally with F#'s pattern matching
Pattern Matching with Map
- TryFind match
let mp = Map.ofList [("doot", 1); ("beef", 2); ("hoopty", 3)]
match Map.tryFind "doot" mp with
| Some value -> printfn "Value: %A" value
| None -> printfn "No value found!"
match Map.tryFind "goot" mp with
| Some value -> printfn "Value: %A" value
| None -> printfn "No value found!"
- TryGetValue match
let mp = Map.ofList [("doot", 1); ("beef", 2); ("hoopty", 3)]
match mp.TryGetValue "doot" with
| (true, value) -> printfn "Value: %A" value
| (false, _) -> printfn "No value found!" // Value: 1
match mp.TryGetValue "goot" with
| (true, value) -> printfn "Value: %A" value
| (false, _) -> printfn "No value found!" // No value found!
Loop Map
- for KeyValue(key, value) in
let map = Map.ofList [("doot", 1); ("beef", 2); ("hoopty", 3)]
for KeyValue(key, value) in map do
printfn "Key: %s, Value: %d" key value
- Map.iter
let map = Map.ofList [("doot", 1); ("beef", 2); ("hoopty", 3)]
map |> Map.iter (fun key value -> printfn "Key: %s, Value: %d" key value)
Modify element of Map
- Add & Remove
let map = Map.ofList [("doot", 1); ("beef", 2); ("hoopty", 3)]
let mapWithAddedElement = map.Add("pork", 4)
printfn "Map after adding (4, \"four\"): %A" mapWithAddedElement
let mapWithRemovedElement = mapWithAddedElement.Remove("beef")
printfn "Map after removing key 2: %A" mapWithRemovedElement
- Change
Map.change key func table
Returns a new map with the value stored under key changed according to func.
let input = Map [ (1, "a"); (2, "b") ]
let changed_map = input |> Map.change 1 (fun x ->
match x with
| Some s -> Some (s + "z")
| None -> None
)
printfn "changed map: %A" changed_map // evaluates to map [(1, "az"); (2, "b")]
Practice
Counting Words in text: "hello world hello map in F#"
let text = "hello world hello map in F#"
let words = text.Split(' ') // ["hello", "world", "hello", "map", "in", "F#"]
let wordCountMap =
words
|> Array.fold (fun acc word ->
if Map.containsKey word acc then // Static Member Function
let count = acc.[word]
acc.Add(word, count + 1)
else
acc.Add(word, 1)
) Map.empty
printfn "Word count map: %A" wordCountMap // Word count map: map [(F#, 1); (hello, 2); (in, 1); (map, 1); (world, 1)]
the solution use Map.containsKey which is Static Member Function, we can use acc Instance Member Function like this:
let text = "hello world hello map in F#"
let words = text.Split(' ')
printfn "words: %A" words
let wordCountMap =
words
|> Array.fold (fun (acc: Map<string, int>) word ->
if acc.ContainsKey word then // Instance Member Function
let count = acc.[word]
acc.Add(word, count + 1)
else
acc.Add(word, 1)
) Map.empty
printfn "Word count map: %A" wordCountMap
Static Member Function and Instance Member Function will be introduced in OOP
Top comments (0)