CRDT Maps
A map is a kind of appendable collection that stores key-value pairs. A collection of elements can be accessed using keys of type string
.
Creation
How to create a map and write to the map:
aqua
-- Creationmap: %u64-- Append different key-value pairsmap <<- "key", 1234map <<- "key", foo()map <<- "second key", foo()
aqua
-- Creationmap: %u64-- Append different key-value pairsmap <<- "key", 1234map <<- "key", foo()map <<- "second key", foo()
Maps can contain only immutable data types elements. Arrows, streams, other maps, closures and abilities are not allowed in the type declarations.
Access
Access to keys and elements can create different behaviours based on tasks.
Get array of elements by a key
Get array of all elements by key:
aqua
values = map.get("key")
aqua
values = map.get("key")
Get stream of elements by a key
Get stream of elements by key, later you can join on the stream to wait for specific number of elements in it:
aqua
valuesStream <- map.getStream("key")-- wait for 9 elementsjoin valuesStream[9]
aqua
valuesStream <- map.getStream("key")-- wait for 9 elementsjoin valuesStream[9]
Get array of keys
Get array of all keys (of type []string
) in map:
aqua
keys <- map.keys()
aqua
keys <- map.keys()
Get stream of keys
Get stream of keys (of type *string
):
aqua
keysStream <- map.keysStream()
aqua
keysStream <- map.keysStream()
Check if a key is presented
Check if key contains at least one element, return boolean:
aqua
keyExist <- map.contains("key")
aqua
keyExist <- map.contains("key")
Difference between methods
get
, keys
and contains
methods return immutable result in-place, on the other hand getStream
and keysStream
methods return streams which will eventually contain updates of the map. For example:
aqua
map: %stringfor p <- peers par:on p:-- add element to map on different peersmap <<- "exec", p-- in this case there is no way to predict what number of elements will be in `results`results = map.get("exec")resultsStream = map.getStream("exec")-- here we can wait for any number of results depends on logic of a program-- let's wait a respond from 5 peersjoin resultsStream[4]-- after join we can be sure that in `resultsStream` will be 5 or more elementsfuncThatNeeds5Responses(resultsStream)
aqua
map: %stringfor p <- peers par:on p:-- add element to map on different peersmap <<- "exec", p-- in this case there is no way to predict what number of elements will be in `results`results = map.get("exec")resultsStream = map.getStream("exec")-- here we can wait for any number of results depends on logic of a program-- let's wait a respond from 5 peersjoin resultsStream[4]-- after join we can be sure that in `resultsStream` will be 5 or more elementsfuncThatNeeds5Responses(resultsStream)
To dive deeper read about join behavior and streams.
Iteration
Maps can be used in for
operations in two ways:
aqua
map: %u64-- key is a string and value is u64for key, value <- map:foo(key, value)for kv <- map:foo(kv.key, kv.value)
aqua
map: %u64-- key is a string and value is u64for key, value <- map:foo(key, value)for kv <- map:foo(kv.key, kv.value)
for
will iterate over key-value pairs by unique keys. Last write win resolution is applied to the values. Order of iteration generally is the same as the order of insertion. Example:
aqua
map: %stringmap <<- "a", "a1"map <<- "b", "b1"map <<- "b", "b2"map <<- "c", "c1"map <<- "c", "c2"map <<- "c", "c3"result: *stringfor k, v <- map:result <<- v
aqua
map: %stringmap <<- "a", "a1"map <<- "b", "b1"map <<- "b", "b2"map <<- "c", "c1"map <<- "c", "c2"map <<- "c", "c3"result: *stringfor k, v <- map:result <<- v
in the end, result
will be ["a1", "b2", "c3"]
.