WhizzML Reference Manual
4.9 Maps
4.9.1 Construction
(make-map list-of-keys list-of-values) \(\rightarrow \) map
make-map is the basic map constructor in WhizzML. This procedure takes two lists of equal length, and keys and values match by position.
(let (v0 3 v1 2) (make-map ["k0" "k1"] [v0 v1])) ;; => {"k0" 3 "k1" 2}
(let (ks ["k1" "k0"]) (make-map ks [v0 v1])) ;; => {"k1" 3 "k0" 2}
The list of keys and values of a map can be recovered via the procedures:
(keys map) \(\rightarrow \) list
(values map) \(\rightarrow \) list
So we have the following identities, for any lists ks and vs:
(= (keys (make-map ks vs)) ks) ;; => true
(= (values (make-map ks vs)) vs) ;; => true
It is also possible to construct a new map using a subset of the keys and values in another one, via select-keys:
(select-keys map list-of-strigs) \(\rightarrow \) map
Non-existing keys are ignored. Thus, for instance:
(select-keys {"a" 2 "b" 12 "c" [1 2]} ["a" "b"]) ;; => {"a" 2 "b" 12}
(select-keys {"a" 2 "b" 12 "c" [1 2]} ["c" "x"]) ;; => {"c" [1 2]}
(select-keys {"a" 2 "b" 12 "c" [1 2]} ["d" "x"]) ;; => {}
(select-keys {"a" 2 "b" 12 "c" [1 2]} []) ;; => {}
4.9.2 Accessors
Access to values in a map is provided by the map itself used as a function (see section 2.5 ), get and get-in, and contains? provides a membership test:
(contains? map str-key) \(\rightarrow \) boolean
(get map str-key [obj]) \(\rightarrow \) object
(get-in map list-of-keys-or-ints [obj]) \(\rightarrow \) object
To access the value associated with a key str-key in a map, we use get, which takes an optional third argument with the value to return in case the key is not found in map. get-in performs a lookup following nested maps, given a list of keys and list positions (for cases where the corresponding value in the path is a list) and, optionally, a default value to return if the element is not found.
(get {"a" 42 "b" 3} "a") ;; => 42
(get {"a" 42} "c" 21) ;; => 21
(get-in {"a" {"b" 34}} ["a" "b"]) ;; => 34
(get-in {"a" [1 2 {"b" [3 4]}]} ["a" 2 "b" 0]) ;; => 3
(contains? {"a" 42} "a") ;; => true
(contains? {"c" {"a" 3}}) ;; => false
However, it is more common to simply apply the map value as a procedure to the key or list of keys we are looking up:
({"a" 42 "b" 3} "a") ;; => 42
({"a" 42} "c" 21) ;; => 21
({"a" {"b" 34}} ["a" "b"]) ;; => 34
({"a" [1 2 {"b" [3 4]}]} ["a" 2 "b" 0]) ;; => 3
Applicability of maps thus makes explicit use of the get and get-in accessors unnecessary in the vast majority of cases 1 .
In order to perform lookups using a path that starts with a list value instead of a map, get-in is actually overloaded and can take a list as its first argument, making the following lookups valid:
([{"b" 2 "a" 22}] [0 "a"]) ;; => 22
([1 [2 [3 [4]]]] [1 1 1 0]) ;; => 4
To avoid undefined values, asking for a key (or key list) that is not contained in a map without providing a default value raises an error with code -15 (key not found).
4.9.3 Element insertion
As all WhizzML values, maps are immutable, but you can create new ones by adding values to an existing one, using assoc, assoc-in and merge:
(assoc map str-key1 obj1 …) \(\rightarrow \) map
(assoc-in map list-of-keys obj) \(\rightarrow \) map
(merge map1 map2) \(\rightarrow \) map
To add key/value pairs to a given map you can use assoc or assoc-in for nested keys. Besides the map, assoc takes an arbitrary number of alternating keys and values, and will raise a bad arity error (code -40) if passed an even number of arguments. merge adds to map1 all keys in map2, overriding any one already in the former.
(assoc {} "key" 42) ;; => {"key" 42}
(assoc {"age" 23} "name" "Johnny") ;; => {"age" 23 "name" "Johnny"}
(assoc {} "key0" {} "key1" [] "key3" 42)
;; => {"key0" {} "key1" [] "key3" 42}
(assoc-in {"foo" 42 "submap" {"k" 3}} ["submap" "m"] 23)
;; => {"foo" 42 "submap" {"k" 3 "m" 23}}
(merge {"a" 2 "b" 3} {"a" 8 "c" 5}) ;; => {"a" 8 "b" 3 "c" 5}
4.9.4 Element removal
One can remove elements from an existing map with dissoc and dissoc-in, which, again, won’t mutate their map arguments, but, rather, return a new map:
(dissoc map str1 …) \(\rightarrow \) map
(dissoc-in map list-of-keys) \(\rightarrow \) map
(dissoc {"a" 1 "b" 2 "c" 3} "a") ;; => {"b" 2 "c" 3}
(dissoc {"a" 1 "b" 2 "c" 3} "a" "c") ;; => {"b" 2}
(dissoc-in {"a" {"b" 3 "c" {"d" 5}}} ["a" "c" "d"])
;; => {"a" {"b" 3 "c" {}}}