| id | stdlib-map |
|---|---|
| title | Map Module |
| slug | /stdlib/map |
FScript maps support string, int, bool, and unions whose cases all have no payload.
Use valueType map[keyType] for key annotations, such as string map[int]. The legacy valueType map suffix means string keys. Keys and values can be inferred without annotations.
The type before map is the value type. The optional brackets specify the key type.
| Annotation | Key type | Value type |
|---|---|---|
int map |
string |
int |
string map[int] |
int |
string |
string map[bool] |
bool |
string |
string map[Priority] |
Priority |
string |
int map[string] is also accepted and displayed as int map. Prefer the shorter form for string keys. Map types compose with other postfix types: int list map[bool] maps booleans to integer lists, and string map[int] list is a list of integer-keyed maps.
type Priority = | Low | High
let names = { ["owner"] = "Ada" }
let byId = { [0] = "toto"; [1] = "titi" }
let byFlag = Map.ofList [(true, "enabled"); (false, "disabled")]
let byPriority = Map.empty |> Map.add Low "later" |> Map.add High "now"
let getZero m = m |> Map.tryGet 0
let first = byId |> getZero |> Option.defaultValue "missing"getZero infers 'v map[int] -> 'v option: the lookup constrains the key type to int, while the value type remains polymorphic. The same helper can read maps of strings or integers. Function arguments do not need annotations for this inference.
All keys in a map must share one type, and all values must share one type. Map.fold, Map.filter, and Map.iter receive keys of the map's actual key type. Map.map and Map.choose preserve that key type. Empty maps acquire their key and value types from use.
An unresolved numeric key is constrained to int. This does not constrain numeric map values:
let make k = { [k + k] = "value" } // k is inferred as int
let numericValues v = { [true] = v + v }
let floatValues = numericValues 2.5When the collection type is unknown, xs[0] infers a list argument. Use Map.tryGet 0 to establish an integer-keyed map without an annotation.
| Form | Signature | Description |
|---|---|---|
values[key] |
'v map['key] -> 'key -> 'v option |
Optional lookup by the map key type. Missing keys return None. |
let maybeA = { ["a"] = 1 }["a"]Empty map.
let m = Map.emptyLooks up a key.
let m = { ["a"] = 1 }
let maybeA = m |> Map.tryGet "a"Checks key existence.
let hasA = { ["a"] = 1 } |> Map.containsKey "a"Adds or replaces a key value.
let updated = Map.add "port" 8080 Map.emptyBuilds a map from key-value tuples. If a key appears more than once, the last value wins.
let m = [("a", 1); ("b", 2)] |> Map.ofListFolds over key-value entries in key order, rather than insertion order. The other map traversals use the same ordering.
let total =
{ ["a"] = 1; ["b"] = 2 }
|> Map.fold (fun acc _ value -> acc + value) 0Returns entry count.
let n = { ["a"] = 1; ["b"] = 2 } |> Map.countKeeps entries that satisfy predicate.
let small =
{ ["a"] = 1; ["b"] = 10 }
|> Map.filter (fun _ v -> v < 5)Transforms entries and drops None results.
let parsed =
{ ["a"] = "1"; ["b"] = "x" }
|> Map.choose (fun _ v -> Int.tryParse v)Transforms values.
let doubled = { ["x"] = 2 } |> Map.map (fun v -> v * 2)Iterates entries for side effects.
{ ["x"] = 2; ["y"] = 3 } |> Map.iter (fun k v -> Console.writeLine $"{k}={v}")Removes a key.
let m = { ["x"] = 2 } |> Map.remove "x"Float, record, list, tuple, and option keys are unsupported. Mixing key types in one map is also invalid. A union key type must have no payload in any of its cases, even if the map uses only a payload-free case.
// Invalid: float key
// let badFloat = { [1.5] = "one" }
// Invalid: mixed key types
// let mixed = { [0] = "zero"; [true] = "yes" }
type Result = | Empty | Value of int
// Invalid: Result has a payload-bearing case
// let badUnion = { [Empty] = "empty" }Json.serialize encodes string, integer, and boolean keys as JSON object property names. It returns None for maps containing union keys.
Map.keys : 'a map['c] -> 'c listReturns keys in the map’s existing deterministic key order.
Map.keys { ["b"]=2; ["a"]=1 }
// ["a";"b"]Map.values : 'a map['c] -> 'a listReturns values in the corresponding deterministic key order.
Map.values { ["b"]=2; ["a"]=1 }
// [1;2]Map.toList : 'a map['c] -> (('c * 'a)) listReturns (key, value) pairs in deterministic key order.
Map.toList { ["a"]=1 }
// [("a", 1)]Map.exists : ('c -> 'a -> bool) -> 'a map['c] -> boolTests entries in key order and stops at the first true predicate result. Empty maps return false.
Map.exists (fun k v -> k="a" && v=1) { ["a"]=1 }
// trueMap.forall : ('c -> 'a -> bool) -> 'a map['c] -> boolTests entries in key order and stops at the first false predicate result. Empty maps return true.
Map.forall (fun k v -> v>0) { ["a"]=1 }
// trueMap.update : 'c -> ('a option -> 'a option) -> 'a map['c] -> 'a map['c]Calls the updater once with the current value option. Some value inserts or replaces the entry; None removes it.
Map.update "a" (fun v -> Some (Option.defaultValue 0 v + 1)) { ["a"]=1 } |> Map.tryGet "a"
// Some 2Map.mergeWith : ('c -> 'a -> 'a -> 'a) -> 'a map['c] -> 'a map['c] -> 'a map['c]Combines both maps. The resolver is called only for shared keys, in right-map key order, with key, left value, then right value. Unique entries are retained.
Map.mergeWith (fun k a b -> a+b) { ["a"]=1 } { ["a"]=2; ["b"]=4 } |> Map.toList
// [("a", 3);("b", 4)]