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_000An
include
file for the different table implementations. 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
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. 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
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. 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
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. 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
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. 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
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 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 inc[A](t: var CountTable[A]; key: A; val = 1)
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
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 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 inc[A](t: CountTableRef[A]; key: A; val = 1)
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–2017 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/tables.html