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, prevent Dict from being serializable. To recover a Dict from an unknown Data, you must first recover an Pairs<k, v> and use dict.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

empty: Dict<a, b>

An empty dictionnary.

dict.to_pairs(dict.empty) == []

Functions

Constructing

from_ascending_pairs(xs: Pairs<ByteArray, value>) -> Dict<key, value>

Like ‘from_pairs’, but from an already sorted list by ascending keys. This function fails (i.e. halts 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

from_pairs(self: Pairs<ByteArray, value>) -> Dict<key, value>

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

find(self: Dict<key, value>, value: value) -> Option<ByteArray>

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(self: Dict<key, value>, key: ByteArray) -> Option<value>

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")

has_key(self: Dict<key, value>, key: ByteArray) -> Bool

Check if a key exists in the dictionary.

let result =
  dict.empty
    |> dict.insert(key: "a", value: "Aiken")
    |> dict.has_key("a")

result == True

is_empty(self: Dict<key, value>) -> Bool

Efficiently checks whether a dictionary is empty.

dict.is_empty(dict.empty) == True

keys(self: Dict<key, value>) -> List<ByteArray>

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"]

size(self: Dict<key, value>) -> Int

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

values(self: Dict<key, value>) -> List<value>

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 == [14, 42, 1337]

Modifying

delete(self: Dict<key, value>, key: ByteArray) -> Dict<key, value>

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)]

filter(
  self: Dict<key, value>,
  with: fn(ByteArray, value) -> Bool,
) -> Dict<key, value>

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(self: Dict<key, value>, key: ByteArray, value: value) -> Dict<key, value>

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)]

map(self: Dict<key, a>, with: fn(ByteArray, a) -> b) -> Dict<key, b>

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)]

pop(self: Dict<key, value>, key: ByteArray) -> (Option<value>, Dict<key, value>)

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

union(left: Dict<key, value>, right: Dict<key, value>) -> Dict<key, value>

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

to_pairs(self: Dict<key, value>) -> Pairs<ByteArray, value>

Get the inner list holding the dictionary data.

let result =
  dict.empty
    |> dict.insert("a", 100)
    |> dict.insert("b", 200)
    |> dict.insert("c", 300)
    |> dict.to_pairs()

result == [Pair("a", 100), Pair("b", 200), Pair("c", 300)]
Search Document