aiken/collection/dict
A module for working with bytearray dictionaries.
Dictionaries are ordered sets of key-value pairs, which thus preserve some invariants. Specifically, each key is only present once in the dictionary and all keys are stored in ascending lexicographic order.
These invariants allow for more optimized functions to operate on
Dict
, but as a trade-offs, preventDict
from being serializable. To recover aDict
from an unknownData
, you must first recover anPairs<k, v>
and usedict.from_ascending_list
.
Types
An opaque Dict
. The type is opaque because the module maintains some
invariant, namely: there’s only one occurrence of a given key in the dictionary.
Note that the key
parameter is a phantom-type, and only present as a
means of documentation. Keys can be any type, yet will need to comparable
to use functions like insert
.
See for example:
pub type Value =
Dict<PolicyId, Dict<AssetName, Int>>
Constants
Functions
Constructing
Like ‘from_pairs’, but from an already sorted list by ascending
keys. This function fails (i.e. halt the program execution) if the list isn’t
sorted.
let pairs = [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
let result =
dict.from_ascending_pairs(pairs)
|> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
This is meant to be used to turn a list constructed off-chain into a Dict
which has taken care of maintaining interval invariants. This function still
performs a sanity check on all keys to avoid silly mistakes. It is, however,
considerably faster than ‘from_pairs’
from_ascending_pairs_with(
xs: Pairs<ByteArray, value>,
predicate: fn(value) -> Bool,
) -> Dict<key, value>
Like from_ascending_pairs
but fails if any
value doesn’t satisfy the predicate.
let pairs = [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
dict.from_ascending_pairs_with(pairs, fn(x) { x <= 250 }) // fail
Construct a dictionary from a list of key-value pairs. Note that when a key is present
multiple times, the first occurrence prevails.
let pairs = [Pair("a", 100), Pair("c", 300), Pair("b", 200)]
let result =
dict.from_pairs(pairs)
|> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
Inspecting
Finds a value in the dictionary, and returns the first key found to have that value.
let result =
dict.empty
|> dict.insert(key: "a", value: 42)
|> dict.insert(key: "b", value: 14)
|> dict.insert(key: "c", value: 42)
|> dict.find(42)
result == Some("a")
Get a value in the dict by its key.
let result =
dict.empty
|> dict.insert(key: "a", value: "Aiken")
|> dict.get(key: "a")
result == Some("Aiken")
Check if a key exists in the dictionary.
let result =
dict.empty
|> dict.insert(key: "a", value: "Aiken")
|> dict.has_key("a")
result == True
Efficiently checks whether a dictionary is empty.
dict.is_empty(dict.empty) == True
Extract all the keys present in a given Dict
.
let result =
dict.empty
|> dict.insert("a", 14)
|> dict.insert("b", 42)
|> dict.insert("a", 1337)
|> dict.keys()
result == ["a", "b"]
Return the number of key-value pairs in the dictionary.
let result =
dict.empty
|> dict.insert("a", 100)
|> dict.insert("b", 200)
|> dict.insert("c", 300)
|> dict.size()
result == 3
Extract all the values present in a given Dict
.
let result =
dict.empty
|> dict.insert("a", 14)
|> dict.insert("b", 42)
|> dict.insert("c", 1337)
|> dict.values()
result == [1337, 42]
Modifying
Remove a key-value pair from the dictionary. If the key is not found, no changes are made.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.delete(key: "a")
|> dict.to_pairs()
result == [Pair("b", 200)]
Keep only the key-value pairs that pass the given predicate.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.filter(fn(k, _v) { k != "a" })
|> dict.to_pairs()
result == [Pair("b", 200), Pair("c", 300)]
Insert a value in the dictionary at a given key. If the key already exists, its value is overridden. If you need ways to combine keys together, use (insert_with
)[#insert_with].
let result =
dict.empty
|> dict.insert(key: "a", value: 1)
|> dict.insert(key: "b", value: 2)
|> dict.insert(key: "a", value: 3)
|> dict.to_pairs()
result == [Pair("a", 3), Pair("b", 2)]
insert_with(
self: Dict<key, value>,
key: ByteArray,
value: value,
with: fn(ByteArray, value, value) -> Option<value>,
) -> Dict<key, value>
Insert a value in the dictionary at a given key. When the key already exist, the provided
merge function is called. The value existing in the dictionary is passed as the second argument
to the merge function, and the new value is passed as the third argument.
let sum =
fn (_k, a, b) { Some(a + b) }
let result =
dict.empty
|> dict.insert_with(key: "a", value: 1, with: sum)
|> dict.insert_with(key: "b", value: 2, with: sum)
|> dict.insert_with(key: "a", value: 3, with: sum)
|> dict.to_pairs()
result == [Pair("a", 4), Pair("b", 2)]
Apply a function to all key-value pairs in a Dict.
let result =
dict.empty
|> dict.insert("a", 100)
|> dict.insert("b", 200)
|> dict.insert("c", 300)
|> dict.map(fn(_k, v) { v * 2 })
|> dict.to_pairs()
result == [Pair("a", 200), Pair("b", 400), Pair("c", 600)]
Remove a key-value pair from the dictionary and return its value. If the key is not found, no changes are made.
let (value, _) =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.pop(key: "a")
result == 100
Combining
Combine two dictionaries. If the same key exist in both the left and
right dictionary, values from the left are preferred (i.e. left-biaised).
let left_dict = dict.from_pairs([Pair("a", 100), Pair("b", 200)])
let right_dict = dict.from_pairs([Pair("a", 150), Pair("c", 300)])
let result =
dict.union(left_dict, right_dict) |> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
union_with(
left: Dict<key, value>,
right: Dict<key, value>,
with: fn(ByteArray, value, value) -> Option<value>,
) -> Dict<key, value>
Like union
but allows specifying the behavior to adopt when a key is present
in both dictionaries. The first value received correspond to the value in the left
dictionnary, whereas the second argument corresponds to the value in the right dictionnary.
When passing None
, the value is removed and not present in the union.
let left_dict = dict.from_pairs([Pair("a", 100), Pair("b", 200)])
let right_dict = dict.from_pairs([Pair("a", 150), Pair("c", 300)])
let result =
dict.union_with(
left_dict,
right_dict,
fn(_k, v1, v2) { Some(v1 + v2) },
)
|> dict.to_pairs()
result == [Pair("a", 250), Pair("b", 200), Pair("c", 300)]
Transforming
foldl(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys
in ascending order and is done from left-to-right.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldl(0, fn(_k, v, r) { v + r })
result == 600
foldr(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys
in ascending order and is done from right-to-left.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldr(0, fn(_k, v, r) { v + r })
result == 600
Like ‘from_pairs’, but from an already sorted list by ascending keys. This function fails (i.e. halt the program execution) if the list isn’t sorted.
let pairs = [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
let result =
dict.from_ascending_pairs(pairs)
|> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
This is meant to be used to turn a list constructed off-chain into a Dict
which has taken care of maintaining interval invariants. This function still
performs a sanity check on all keys to avoid silly mistakes. It is, however,
considerably faster than ‘from_pairs’
from_ascending_pairs_with(
xs: Pairs<ByteArray, value>,
predicate: fn(value) -> Bool,
) -> Dict<key, value>
Like from_ascending_pairs
but fails if any
value doesn’t satisfy the predicate.
let pairs = [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
dict.from_ascending_pairs_with(pairs, fn(x) { x <= 250 }) // fail
Construct a dictionary from a list of key-value pairs. Note that when a key is present multiple times, the first occurrence prevails.
let pairs = [Pair("a", 100), Pair("c", 300), Pair("b", 200)]
let result =
dict.from_pairs(pairs)
|> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
Finds a value in the dictionary, and returns the first key found to have that value.
let result =
dict.empty
|> dict.insert(key: "a", value: 42)
|> dict.insert(key: "b", value: 14)
|> dict.insert(key: "c", value: 42)
|> dict.find(42)
result == Some("a")
Get a value in the dict by its key.
let result =
dict.empty
|> dict.insert(key: "a", value: "Aiken")
|> dict.get(key: "a")
result == Some("Aiken")
Check if a key exists in the dictionary.
let result =
dict.empty
|> dict.insert(key: "a", value: "Aiken")
|> dict.has_key("a")
result == True
Efficiently checks whether a dictionary is empty.
dict.is_empty(dict.empty) == True
Extract all the keys present in a given Dict
.
let result =
dict.empty
|> dict.insert("a", 14)
|> dict.insert("b", 42)
|> dict.insert("a", 1337)
|> dict.keys()
result == ["a", "b"]
Return the number of key-value pairs in the dictionary.
let result =
dict.empty
|> dict.insert("a", 100)
|> dict.insert("b", 200)
|> dict.insert("c", 300)
|> dict.size()
result == 3
Extract all the values present in a given Dict
.
let result =
dict.empty
|> dict.insert("a", 14)
|> dict.insert("b", 42)
|> dict.insert("c", 1337)
|> dict.values()
result == [1337, 42]
Modifying
Remove a key-value pair from the dictionary. If the key is not found, no changes are made.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.delete(key: "a")
|> dict.to_pairs()
result == [Pair("b", 200)]
Keep only the key-value pairs that pass the given predicate.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.filter(fn(k, _v) { k != "a" })
|> dict.to_pairs()
result == [Pair("b", 200), Pair("c", 300)]
Insert a value in the dictionary at a given key. If the key already exists, its value is overridden. If you need ways to combine keys together, use (insert_with
)[#insert_with].
let result =
dict.empty
|> dict.insert(key: "a", value: 1)
|> dict.insert(key: "b", value: 2)
|> dict.insert(key: "a", value: 3)
|> dict.to_pairs()
result == [Pair("a", 3), Pair("b", 2)]
insert_with(
self: Dict<key, value>,
key: ByteArray,
value: value,
with: fn(ByteArray, value, value) -> Option<value>,
) -> Dict<key, value>
Insert a value in the dictionary at a given key. When the key already exist, the provided
merge function is called. The value existing in the dictionary is passed as the second argument
to the merge function, and the new value is passed as the third argument.
let sum =
fn (_k, a, b) { Some(a + b) }
let result =
dict.empty
|> dict.insert_with(key: "a", value: 1, with: sum)
|> dict.insert_with(key: "b", value: 2, with: sum)
|> dict.insert_with(key: "a", value: 3, with: sum)
|> dict.to_pairs()
result == [Pair("a", 4), Pair("b", 2)]
Apply a function to all key-value pairs in a Dict.
let result =
dict.empty
|> dict.insert("a", 100)
|> dict.insert("b", 200)
|> dict.insert("c", 300)
|> dict.map(fn(_k, v) { v * 2 })
|> dict.to_pairs()
result == [Pair("a", 200), Pair("b", 400), Pair("c", 600)]
Remove a key-value pair from the dictionary and return its value. If the key is not found, no changes are made.
let (value, _) =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.pop(key: "a")
result == 100
Combining
Combine two dictionaries. If the same key exist in both the left and
right dictionary, values from the left are preferred (i.e. left-biaised).
let left_dict = dict.from_pairs([Pair("a", 100), Pair("b", 200)])
let right_dict = dict.from_pairs([Pair("a", 150), Pair("c", 300)])
let result =
dict.union(left_dict, right_dict) |> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
union_with(
left: Dict<key, value>,
right: Dict<key, value>,
with: fn(ByteArray, value, value) -> Option<value>,
) -> Dict<key, value>
Like union
but allows specifying the behavior to adopt when a key is present
in both dictionaries. The first value received correspond to the value in the left
dictionnary, whereas the second argument corresponds to the value in the right dictionnary.
When passing None
, the value is removed and not present in the union.
let left_dict = dict.from_pairs([Pair("a", 100), Pair("b", 200)])
let right_dict = dict.from_pairs([Pair("a", 150), Pair("c", 300)])
let result =
dict.union_with(
left_dict,
right_dict,
fn(_k, v1, v2) { Some(v1 + v2) },
)
|> dict.to_pairs()
result == [Pair("a", 250), Pair("b", 200), Pair("c", 300)]
Transforming
foldl(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys
in ascending order and is done from left-to-right.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldl(0, fn(_k, v, r) { v + r })
result == 600
foldr(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys
in ascending order and is done from right-to-left.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldr(0, fn(_k, v, r) { v + r })
result == 600
Remove a key-value pair from the dictionary. If the key is not found, no changes are made.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.delete(key: "a")
|> dict.to_pairs()
result == [Pair("b", 200)]
Keep only the key-value pairs that pass the given predicate.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.filter(fn(k, _v) { k != "a" })
|> dict.to_pairs()
result == [Pair("b", 200), Pair("c", 300)]
Insert a value in the dictionary at a given key. If the key already exists, its value is overridden. If you need ways to combine keys together, use (insert_with
)[#insert_with].
let result =
dict.empty
|> dict.insert(key: "a", value: 1)
|> dict.insert(key: "b", value: 2)
|> dict.insert(key: "a", value: 3)
|> dict.to_pairs()
result == [Pair("a", 3), Pair("b", 2)]
insert_with(
self: Dict<key, value>,
key: ByteArray,
value: value,
with: fn(ByteArray, value, value) -> Option<value>,
) -> Dict<key, value>
Insert a value in the dictionary at a given key. When the key already exist, the provided merge function is called. The value existing in the dictionary is passed as the second argument to the merge function, and the new value is passed as the third argument.
let sum =
fn (_k, a, b) { Some(a + b) }
let result =
dict.empty
|> dict.insert_with(key: "a", value: 1, with: sum)
|> dict.insert_with(key: "b", value: 2, with: sum)
|> dict.insert_with(key: "a", value: 3, with: sum)
|> dict.to_pairs()
result == [Pair("a", 4), Pair("b", 2)]
Apply a function to all key-value pairs in a Dict.
let result =
dict.empty
|> dict.insert("a", 100)
|> dict.insert("b", 200)
|> dict.insert("c", 300)
|> dict.map(fn(_k, v) { v * 2 })
|> dict.to_pairs()
result == [Pair("a", 200), Pair("b", 400), Pair("c", 600)]
Remove a key-value pair from the dictionary and return its value. If the key is not found, no changes are made.
let (value, _) =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.pop(key: "a")
result == 100
Combine two dictionaries. If the same key exist in both the left and right dictionary, values from the left are preferred (i.e. left-biaised).
let left_dict = dict.from_pairs([Pair("a", 100), Pair("b", 200)])
let right_dict = dict.from_pairs([Pair("a", 150), Pair("c", 300)])
let result =
dict.union(left_dict, right_dict) |> dict.to_pairs()
result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
union_with(
left: Dict<key, value>,
right: Dict<key, value>,
with: fn(ByteArray, value, value) -> Option<value>,
) -> Dict<key, value>
Like union
but allows specifying the behavior to adopt when a key is present
in both dictionaries. The first value received correspond to the value in the left
dictionnary, whereas the second argument corresponds to the value in the right dictionnary.
When passing None
, the value is removed and not present in the union.
let left_dict = dict.from_pairs([Pair("a", 100), Pair("b", 200)])
let right_dict = dict.from_pairs([Pair("a", 150), Pair("c", 300)])
let result =
dict.union_with(
left_dict,
right_dict,
fn(_k, v1, v2) { Some(v1 + v2) },
)
|> dict.to_pairs()
result == [Pair("a", 250), Pair("b", 200), Pair("c", 300)]
Transforming
foldl(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys
in ascending order and is done from left-to-right.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldl(0, fn(_k, v, r) { v + r })
result == 600
foldr(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys
in ascending order and is done from right-to-left.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldr(0, fn(_k, v, r) { v + r })
result == 600
foldl(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys in ascending order and is done from left-to-right.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldl(0, fn(_k, v, r) { v + r })
result == 600
foldr(
self: Dict<key, value>,
zero: result,
with: fn(ByteArray, value, result) -> result,
) -> result
Fold over the key-value pairs in a dictionary. The fold direction follows keys in ascending order and is done from right-to-left.
let result =
dict.empty
|> dict.insert(key: "a", value: 100)
|> dict.insert(key: "b", value: 200)
|> dict.insert(key: "c", value: 300)
|> dict.foldr(0, fn(_k, v, r) { v + r })
result == 600