The tables module implements variants of an efficient hash table (also often named dictionary in other programming languages) that is a mapping from keys to values. Table is the usual hash table, OrderedTable is like Table but remembers insertion order and CountTable is a mapping from a key to its number of occurrences. For consistency with every other data type in Nim these have value semantics, this means that = performs a copy of the hash table. For reference semantics use the Ref variant: TableRef, OrderedTableRef, CountTableRef. To give an example, when a is a Table, then var b = a gives b as a new independent table. b is initialised with the contents of a. Changing b does not affect a and vice versa:
import tables
var
a = {1: "one", 2: "two"}.toTable # creates a Table
b = a
echo a, b # output: {1: one, 2: two}{1: one, 2: two}
b[3] = "three"
echo a, b # output: {1: one, 2: two}{1: one, 2: two, 3: three}
echo a == b # output: false
On the other hand, when a is a TableRef instead, then changes to b also affect a. Both a and b reference the same data structure:
import tables
var
a = {1: "one", 2: "two"}.newTable # creates a TableRef
b = a
echo a, b # output: {1: one, 2: two}{1: one, 2: two}
b[3] = "three"
echo a, b # output: {1: one, 2: two, 3: three}{1: one, 2: two, 3: three}
echo a == b # output: true
If you are using simple standard types like int or string for the keys of the table you won't have any problems, but as soon as you try to use a more complex object as a key you will be greeted by a strange compiler error:
Error: type mismatch: got (Person) but expected one of: hashes.hash(x: openarray[A]): Hash hashes.hash(x: int): Hash hashes.hash(x: float): Hash …
What is happening here is that the types used for table keys require to have a hash() proc which will convert them to a Hash value, and the compiler is listing all the hash functions it knows. Additionally there has to be a == operator that provides the same semantics as its corresponding hash proc.
After you add hash and == for your custom type everything will work. Currently however hash for objects is not defined, whereas system.== for objects does exist and performs a "deep" comparison (every field is compared) which is usually what you want. So in the following example implementing only hash suffices:
type
Person = object
firstName, lastName: string
proc hash(x: Person): Hash =
## Piggyback on the already available string hash proc.
##
## Without this proc nothing works!
result = x.firstName.hash !& x.lastName.hash
result = !$result
var
salaries = initTable[Person, int]()
p1, p2: Person
p1.firstName = "Jon"
p1.lastName = "Ross"
salaries[p1] = 30_000
p2.firstName = "소진"
p2.lastName = "박"
salaries[p2] = 45_000 Table[A; B] = object data: KeyValuePairSeq[A, B] counter: int
TableRef[A; B] = ref Table[A, B]
OrderedTable[A; B] = object data: OrderedKeyValuePairSeq[A, B] counter, first, last: int
OrderedTableRef[A; B] = ref OrderedTable[A, B]
CountTable[A] = object data: seq[tuple[key: A, val: int]] counter: int
CountTableRef[A] = ref CountTable[A]
proc clear[A, B](t: var Table[A, B])
proc clear[A, B](t: TableRef[A, B])
proc rightSize(count: Natural): int {...}{.inline, raises: [], tags: [].}Return the value of initialSize to support count items.
If more items are expected to be added, simply add that expected extra amount to the parameter before calling this.
Internally, we want mustRehash(rightSize(x), x) == false.
proc len[A, B](t: Table[A, B]): int
proc `[]`[A, B](t: Table[A, B]; key: A): B {...}{..}t[key]. If key is not in t, the KeyError exception is raised. One can check with hasKey whether the key exists. proc `[]`[A, B](t: var Table[A, B]; key: A): var B {...}{..}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. proc mget[A, B](t: var Table[A, B]; key: A): var B {...}{.deprecated.}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. Use ```[]``` instead. proc getOrDefault[A, B](t: Table[A, B]; key: A): B
t[key] iff key is in t. Otherwise, the default initialization value for type B is returned (e.g. 0 for any integer type). proc getOrDefault[A, B](t: Table[A, B]; key: A; default: B): B
t[key] iff key is in t. Otherwise, default is returned. proc hasKey[A, B](t: Table[A, B]; key: A): bool
proc contains[A, B](t: Table[A, B]; key: A): bool
proc del[A, B](t: var Table[A, B]; key: A)
proc take[A, B](t: var Table[A, B]; key: A; val: var B): bool
key from the table. Returns true, if the key existed, and sets val to the mapping of the key. Otherwise, returns false, and the val is unchanged. proc mgetOrPut[A, B](t: var Table[A, B]; key: A; val: B): var B
t[key] or puts val if not present, either way returning a value which can be modified. proc hasKeyOrPut[A, B](t: var Table[A, B]; key: A; val: B): bool
proc `[]=`[A, B](t: var Table[A, B]; key: A; val: B)
proc add[A, B](t: var Table[A, B]; key: A; val: B)
t[key] already exists. This can introduce duplicate keys into the table! proc len[A, B](t: TableRef[A, B]): int
proc initTable[A, B](initialSize = 64): Table[A, B]
creates a new hash table that is empty.
initialSize needs to be a power of two. If you need to accept runtime values for this you could use the nextPowerOfTwo proc from the math module or the rightSize proc from this module.
proc toTable[A, B](pairs: openArray[(A, B)]): Table[A, B]
proc `$`[A, B](t: Table[A, B]): string
proc hasKey[A, B](t: TableRef[A, B]; key: A): bool
proc `==`[A, B](s, t: Table[A, B]): bool
true iff the content of both tables contains the same key-value pairs. Insert order does not matter. proc indexBy[A, B, C](collection: A; index: proc (x: B): C): Table[C, B]
proc `[]`[A, B](t: TableRef[A, B]; key: A): var B {...}{..}t[key]. If key is not in t, the KeyError exception is raised. One can check with hasKey whether the key exists. proc mget[A, B](t: TableRef[A, B]; key: A): var B {...}{.deprecated.}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. Use ```[]``` instead. proc getOrDefault[A, B](t: TableRef[A, B]; key: A): B
t[key] iff key is in t. Otherwise, the default initialization value for type B is returned (e.g. 0 for any integer type). proc getOrDefault[A, B](t: TableRef[A, B]; key: A; default: B): B
t[key] iff key is in t. Otherwise, default is returned. proc mgetOrPut[A, B](t: TableRef[A, B]; key: A; val: B): var B
t[key] or puts val if not present, either way returning a value which can be modified. proc hasKeyOrPut[A, B](t: var TableRef[A, B]; key: A; val: B): bool
proc contains[A, B](t: TableRef[A, B]; key: A): bool
proc `[]=`[A, B](t: TableRef[A, B]; key: A; val: B)
proc add[A, B](t: TableRef[A, B]; key: A; val: B)
t[key] already exists. This can introduce duplicate keys into the table! proc del[A, B](t: TableRef[A, B]; key: A)
proc take[A, B](t: TableRef[A, B]; key: A; val: var B): bool
key from the table. Returns true, if the key existed, and sets val to the mapping of the key. Otherwise, returns false, and the val is unchanged. proc newTable[A, B](initialSize = 64): TableRef[A, B]
proc newTable[A, B](pairs: openArray[(A, B)]): TableRef[A, B]
proc `$`[A, B](t: TableRef[A, B]): string
proc `==`[A, B](s, t: TableRef[A, B]): bool
true iff either both tables are nil or none is nil and the content of both tables contains the same key-value pairs. Insert order does not matter. proc newTableFrom[A, B, C](collection: A; index: proc (x: B): C): TableRef[C, B]
proc len[A, B](t: OrderedTable[A, B]): int {...}{.inline.}proc clear[A, B](t: var OrderedTable[A, B])
proc clear[A, B](t: var OrderedTableRef[A, B])
proc `[]`[A, B](t: OrderedTable[A, B]; key: A): B {...}{..}t[key]. If key is not in t, the KeyError exception is raised. One can check with hasKey whether the key exists. proc `[]`[A, B](t: var OrderedTable[A, B]; key: A): var B {...}{..}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. proc mget[A, B](t: var OrderedTable[A, B]; key: A): var B {...}{.deprecated.}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. Use ```[]``` instead. proc getOrDefault[A, B](t: OrderedTable[A, B]; key: A): B
t[key] iff key is in t. Otherwise, the default initialization value for type B is returned (e.g. 0 for any integer type). proc getOrDefault[A, B](t: OrderedTable[A, B]; key: A; default: B): B
t[key] iff key is in t. Otherwise, default is returned. proc hasKey[A, B](t: OrderedTable[A, B]; key: A): bool
proc contains[A, B](t: OrderedTable[A, B]; key: A): bool
proc `[]=`[A, B](t: var OrderedTable[A, B]; key: A; val: B)
proc add[A, B](t: var OrderedTable[A, B]; key: A; val: B)
t[key] already exists. This can introduce duplicate keys into the table! proc mgetOrPut[A, B](t: var OrderedTable[A, B]; key: A; val: B): var B
t[key] or puts value if not present, either way returning a value which can be modified. proc hasKeyOrPut[A, B](t: var OrderedTable[A, B]; key: A; val: B): bool
proc initOrderedTable[A, B](initialSize = 64): OrderedTable[A, B]
creates a new ordered hash table that is empty.
initialSize needs to be a power of two. If you need to accept runtime values for this you could use the nextPowerOfTwo proc from the math module or the rightSize proc from this module.
proc toOrderedTable[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B]
proc `$`[A, B](t: OrderedTable[A, B]): string
proc `==`[A, B](s, t: OrderedTable[A, B]): bool
proc sort[A, B](t: var OrderedTable[A, B]; cmp: proc (x, y: (A, B)): int)
proc len[A, B](t: OrderedTableRef[A, B]): int {...}{.inline.}proc `[]`[A, B](t: OrderedTableRef[A, B]; key: A): var B
t[key]. If key is not in t, the KeyError exception is raised. One can check with hasKey whether the key exists. proc mget[A, B](t: OrderedTableRef[A, B]; key: A): var B {...}{.deprecated.}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. Use ```[]``` instead. proc getOrDefault[A, B](t: OrderedTableRef[A, B]; key: A): B
t[key] iff key is in t. Otherwise, the default initialization value for type B is returned (e.g. 0 for any integer type). proc getOrDefault[A, B](t: OrderedTableRef[A, B]; key: A; default: B): B
t[key] iff key is in t. Otherwise, default is returned. proc mgetOrPut[A, B](t: OrderedTableRef[A, B]; key: A; val: B): var B
t[key] or puts val if not present, either way returning a value which can be modified. proc hasKeyOrPut[A, B](t: var OrderedTableRef[A, B]; key: A; val: B): bool
proc hasKey[A, B](t: OrderedTableRef[A, B]; key: A): bool
proc contains[A, B](t: OrderedTableRef[A, B]; key: A): bool
proc `[]=`[A, B](t: OrderedTableRef[A, B]; key: A; val: B)
proc add[A, B](t: OrderedTableRef[A, B]; key: A; val: B)
t[key] already exists. This can introduce duplicate keys into the table! proc newOrderedTable[A, B](initialSize = 64): OrderedTableRef[A, B]
creates a new ordered hash table that is empty.
initialSize needs to be a power of two. If you need to accept runtime values for this you could use the nextPowerOfTwo proc from the math module or the rightSize proc from this module.
proc newOrderedTable[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B]
proc `$`[A, B](t: OrderedTableRef[A, B]): string
proc `==`[A, B](s, t: OrderedTableRef[A, B]): bool
nil or none is nil and the content and the order of both are equal. proc sort[A, B](t: OrderedTableRef[A, B]; cmp: proc (x, y: (A, B)): int)
proc del[A, B](t: var OrderedTable[A, B]; key: A)
proc del[A, B](t: var OrderedTableRef[A, B]; key: A)
proc len[A](t: CountTable[A]): int
proc clear[A](t: CountTableRef[A])
proc clear[A](t: var CountTable[A])
proc `[]`[A](t: CountTable[A]; key: A): int {...}{..}t[key]. If key is not in t, the KeyError exception is raised. One can check with hasKey whether the key exists. proc `[]`[A](t: var CountTable[A]; key: A): var int {...}{..}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. proc mget[A](t: var CountTable[A]; key: A): var int {...}{.deprecated.}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. Use ```[]``` instead. proc getOrDefault[A](t: CountTable[A]; key: A): int
t[key] iff key is in t. Otherwise, 0 (the default initialization value of int), is returned. proc getOrDefault[A](t: CountTable[A]; key: A; default: int): int
t[key] iff key is in t. Otherwise, the integer value of default is returned. proc hasKey[A](t: CountTable[A]; key: A): bool
proc contains[A](t: CountTable[A]; key: A): bool
proc `[]=`[A](t: var CountTable[A]; key: A; val: int)
proc inc[A](t: var CountTable[A]; key: A; val = 1)
proc initCountTable[A](initialSize = 64): CountTable[A]
creates a new count table that is empty.
initialSize needs to be a power of two. If you need to accept runtime values for this you could use the nextPowerOfTwo proc from the math module or the rightSize proc in this module.
proc toCountTable[A](keys: openArray[A]): CountTable[A]
proc `$`[A](t: CountTable[A]): string
proc `==`[A](s, t: CountTable[A]): bool
true iff both tables contain the same keys with the same count. Insert order does not matter. proc smallest[A](t: CountTable[A]): tuple[key: A, val: int]
proc largest[A](t: CountTable[A]): tuple[key: A, val: int]
proc sort[A](t: var CountTable[A])
proc len[A](t: CountTableRef[A]): int
proc `[]`[A](t: CountTableRef[A]; key: A): var int {...}{..}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. proc mget[A](t: CountTableRef[A]; key: A): var int {...}{.deprecated.}t[key]. The value can be modified. If key is not in t, the KeyError exception is raised. Use ```[]``` instead. proc getOrDefault[A](t: CountTableRef[A]; key: A): int
t[key] iff key is in t. Otherwise, 0 (the default initialization value of int), is returned. proc getOrDefault[A](t: CountTableRef[A]; key: A; default: int): int
t[key] iff key is in t. Otherwise, the integer value of default is returned. proc hasKey[A](t: CountTableRef[A]; key: A): bool
proc contains[A](t: CountTableRef[A]; key: A): bool
proc `[]=`[A](t: CountTableRef[A]; key: A; val: int)
proc inc[A](t: CountTableRef[A]; key: A; val = 1)
proc newCountTable[A](initialSize = 64): CountTableRef[A]
creates a new count table that is empty.
initialSize needs to be a power of two. If you need to accept runtime values for this you could use the nextPowerOfTwo proc from the math module or the rightSize method in this module.
proc newCountTable[A](keys: openArray[A]): CountTableRef[A]
proc `$`[A](t: CountTableRef[A]): string
proc `==`[A](s, t: CountTableRef[A]): bool
true iff either both tables are nil or none is nil and both contain the same keys with the same count. Insert order does not matter. proc smallest[A](t: CountTableRef[A]): (A, int)
proc largest[A](t: CountTableRef[A]): (A, int)
proc sort[A](t: CountTableRef[A])
proc merge[A](s: var CountTable[A]; t: CountTable[A])
proc merge[A](s, t: CountTable[A]): CountTable[A]
proc merge[A](s, t: CountTableRef[A])
iterator allValues[A, B](t: Table[A, B]; key: A): B
iterator pairs[A, B](t: Table[A, B]): (A, B)
iterator mpairs[A, B](t: var Table[A, B]): (A, var B)
iterator keys[A, B](t: Table[A, B]): A
iterator values[A, B](t: Table[A, B]): B
iterator mvalues[A, B](t: var Table[A, B]): var B
iterator pairs[A, B](t: TableRef[A, B]): (A, B)
iterator mpairs[A, B](t: TableRef[A, B]): (A, var B)
iterator keys[A, B](t: TableRef[A, B]): A
iterator values[A, B](t: TableRef[A, B]): B
iterator mvalues[A, B](t: TableRef[A, B]): var B
iterator pairs[A, B](t: OrderedTable[A, B]): (A, B)
iterator mpairs[A, B](t: var OrderedTable[A, B]): (A, var B)
iterator keys[A, B](t: OrderedTable[A, B]): A
iterator values[A, B](t: OrderedTable[A, B]): B
iterator mvalues[A, B](t: var OrderedTable[A, B]): var B
iterator pairs[A, B](t: OrderedTableRef[A, B]): (A, B)
iterator mpairs[A, B](t: OrderedTableRef[A, B]): (A, var B)
iterator keys[A, B](t: OrderedTableRef[A, B]): A
iterator values[A, B](t: OrderedTableRef[A, B]): B
iterator mvalues[A, B](t: OrderedTableRef[A, B]): var B
iterator pairs[A](t: CountTable[A]): (A, int)
iterator mpairs[A](t: var CountTable[A]): (A, var int)
iterator keys[A](t: CountTable[A]): A
iterator values[A](t: CountTable[A]): int
iterator mvalues[A](t: CountTable[A]): var int
iterator pairs[A](t: CountTableRef[A]): (A, int)
iterator mpairs[A](t: CountTableRef[A]): (A, var int)
iterator keys[A](t: CountTableRef[A]): A
iterator values[A](t: CountTableRef[A]): int
iterator mvalues[A](t: CountTableRef[A]): var int
template withValue[A; B](t: var Table[A, B]; key: A; value, body: untyped)
t[key]. value can be modified in the scope of the withValue call.sharedTable.withValue(key, value) do: # block is executed only if ``key`` in ``t`` value.name = "username" value.uid = 1000
template withValue[A; B](t: var Table[A, B]; key: A; value, body1, body2: untyped)
t[key]. value can be modified in the scope of the withValue call.table.withValue(key, value) do: # block is executed only if ``key`` in ``t`` value.name = "username" value.uid = 1000 do: # block is executed when ``key`` not in ``t`` raise newException(KeyError, "Key not found")
© 2006–2018 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/tables.html