Hash Table
A hash table is a data structure that maps keys to values. It works by feeding each key through a hash function to produce an integer, which is then used to compute an index into an internal array of slots, allowing lookups, insertions, and removals in approximately constant time. The type of a hash table mapping keys of type K to values of type V is written Table(K, V), and lives in the "Hash_Table" module.
#import "Hash_Table";
inventory: Table(string, int);
table_add(*inventory, "iron_ore", 64);
table_add(*inventory, "oak_log", 32);
success, value := table_find(*inventory, "iron_ore");
if success print("iron_ore count is %\n", value); // 64
Adding and Setting Entries
Use table_add to insert a key-value pair. It returns a pointer to the inserted value. If you add the same key twice, both entries will exist and lookups will return the first match. Use table_set instead if you want at most one entry per key; it replaces the value if the key already exists.
table_add(*inventory, "diamond", 3); // Always inserts a new entry.
table_set(*inventory, "iron_ore", 128); // Replaces iron_ore's existing count.
table_set(*inventory, "emerald", 7); // Adds emerald, since it wasn't in the table.
Looking Up Values
Use table_find to look up a key. It returns both a success boolean and the value. The success return is marked #must, so the compiler will error if you discard it. If success is false, the returned value is uninitialized and must not be used. Use table_find_pointer if you need a pointer to the value, for example to modify it in place. It returns null if the key is not found.
success, value := table_find(*inventory, "iron_ore");
if success print("iron_ore count is %\n", value);
ptr := table_find_pointer(*inventory, "oak_log");
if ptr ptr.* += 16; // Add 16 more oak logs.
Checking for a Key
Use table_contains when you only need to know whether a key exists. This is useful when using a table as a set:
if table_contains(*inventory, "diamond") {
print("We have diamonds.\n");
}
Removing Entries
Use table_remove to remove the first entry matching a given key. It returns a success boolean and the value that was removed:
success, removed_value := table_remove(*inventory, "oak_log");
if success print("Removed oak_log with count %\n", removed_value);
Find or Add
Use find_or_add when you want a pointer to a value for a given key, creating a default-initialized entry if the key does not yet exist:
entry, newly_added := find_or_add(*inventory, "gold_ingot");
if newly_added entry.* = 10;
Iteration
Hash tables can be iterated with a for loop. The loop variable it holds the value, and it_index holds the key. The iteration order is not guaranteed.
for inventory {
print("% has count %\n", it_index, it);
}
You can rename it using the standard named iterator syntax, but it_index cannot be renamed directly. If you want both the key and value under custom names, assign them inside the loop body:
for inventory {
item, count := it_index, it;
print("% has count %\n", item, count);
}
You can iterate by pointer to modify values in place, and you can remove entries during iteration:
for * inventory {
it.* += 1; // Add one of every item.
}
for inventory {
if it < 5 remove it; // Remove items with fewer than 5 in stock.
}
You can rename it and it_index using the standard named iterator syntax. Place the value name first, then the key name, separated by a comma, before the ::
for count, item : inventory {
print("% has count %\n", item, count);
}
This also combines with pointer iteration, letting you modify values in place while using custom names:
for * qty, item : inventory {
print("Restocking %\n", item);
qty.* += 10; // Restock 10 of every item.
}
You can also iterate by pointer or remove entries without custom names:
for * inventory {
it.* += 1; // Add one of every item.
}
for inventory {
if it < 5 remove it; // Remove items with fewer than 5 in stock.
}
Resetting and Freeing
Use table_reset to clear all entries while keeping the allocated memory. Use deinit to free all memory owned by the table:
table_reset(*inventory); // count is now 0, memory still allocated.
deinit(*inventory); // All memory freed.