Logo

dev-resources.site

for different kinds of informations.

F# For Dummys - Day 14 Collections Map

Published at
5/25/2024
Categories
fsharp
Author
pythonzhu
Categories
1 categories in total
fsharp
open
Author
9 person written this
pythonzhu
open
F# For Dummys - Day 14 Collections Map

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)]
Enter fullscreen mode Exit fullscreen mode
  • Map.ofList
let map = 
    Map.ofList [
        (3, "three")
        (1, "one")
        (2, "two")
    ]

printfn "map: %A" map // map [(1, one); (2, two); (3, three)]
Enter fullscreen mode Exit fullscreen mode

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)]
Enter fullscreen mode Exit fullscreen mode

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 []
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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)
Enter fullscreen mode Exit fullscreen mode

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!"
Enter fullscreen mode Exit fullscreen mode
  • 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!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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)
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode
  • 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")]
Enter fullscreen mode Exit fullscreen mode

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)]
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Static Member Function and Instance Member Function will be introduced in OOP

fsharp Article's
30 articles in total
Favicon
Learning some Fantomas AST
Favicon
Ingesting Data in F# with Aether: A Practical Guide to Using Lenses, Prisms, and Morphisms
Favicon
Suicide Boys Merch quality designed shop
Favicon
F# 9: Nullable Reference Types and Advancing Null Safety
Favicon
Unlocking High-Performance AI Computing with F#: A Comprehensive Guide
Favicon
Scope progression
Favicon
How do I register a complaint with Delhivery?
Favicon
New wallpapers every day
Favicon
F# šŸ¤ GTK4
Favicon
Introducing F# with Semantic Kernel: Simplifying AI App Development with the Pipeline Pattern
Favicon
Describing musical domain with F#
Favicon
Who's Your .NET Ally? - F# vs C#
Favicon
F# For Dummys - Day 13 Collections Array
Favicon
F# For Dummys - Day 12 Collections List
Favicon
Primitive Type Differentiation in F#
Favicon
Request -> Handler -> SubPub Pattern with MediatR or Raw F# code
Favicon
F# For Dummys - Day 11 Collections Tuple
Favicon
F# For Dummys - Day 9 Branching
Favicon
F# For Dummys - Day 8 Function && Pipeline
Favicon
F# For Dummys - Day 7 Operators
Favicon
F# For Dummys - Day 16 Collections Sequence
Favicon
F# For Dummys - Day 15 Collections Set
Favicon
F# For Dummys - Day 14 Collections Map
Favicon
F# For Dummys - Day 10 Loop
Favicon
F# For Dummys - Day 6 Format
Favicon
F# For Dummys - Day 5 Mutable
Favicon
F# For Dummys - Day 4 Value
Favicon
F# For Dummys - Day 3 New Program
Favicon
F# For Dummys - Day 2 Environment
Favicon
F# For Dummys - Day 0 Foreword

Featured ones: