Collections
Nim's standard library offers a small, well-chosen set of containers: fixed arrays, dynamic sequences, sets of ordinals, hash sets and hash tables. Choosing the right one is mostly a question of whether the size is known at compile time and whether you need lookup by key.
Arrays and Sequences
An array has a size fixed at compile time and no heap allocation. A seq grows at runtime and is the default choice for lists of things.
Fixed-Size Arrays
import std/strutils
var week: array[7, string] = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
echo week.len, " days" # array length is part of the type, known at compile time
echo week[0], " -> ", week[^1] # '^1' means the last element, counting from the end
# Iterating with an index is unnecessary when you only need the values.
for day in week:
if day.len == 6 and day.endsWith("day"):
echo "practice day: ", day
# Arrays can be nested: a grid stored inline, with no dynamic allocation.
var grid: array[3, array[3, int]]
grid[1][2] = 7 # rows first, columns second
echo grid[1][2]
Dynamic Sequences
var nums = @[3, 1, 2] # '@[...]' builds a seq; the type is seq[int]
nums.add(4) # append to the end — amortized constant time
nums.insert(0) # insert at index 0 (the default): shifts the rest
echo nums.len, " ", nums # 5 @[0, 3, 1, 2, 4]
nums.delete(0) # remove by index; order of the rest is preserved
echo nums # @[3, 1, 2, 4]
# Slicing copies a sub-range; '^1' addresses the final element.
let tail = nums[1 .. ^1]
echo tail # @[1, 2, 4]
# newSeq allocates a known size up front, avoiding repeated reallocation.
var zeros = newSeq[int](4)
zeros[2] = 9
echo zeros # @[0, 0, 9, 0]
# add can move a whole seq at once, which is how small collections compose.
zeros.add(nums[0 .. 1])
echo zeros # @[0, 0, 9, 0, 3, 1]
Sets, Hash Sets and Tables
A set is a bitset over a small ordinal type — compact, fast and allocation-free. When the domain is not a small ordinal, use HashSet; when values must be looked up by key, use Table.
Sets of Ordinals
type Flag = enum flRead, flWrite, flExec
var permissions: set[Flag] = {flRead} # a set literal uses braces
permissions.incl(flWrite) # add a member
permissions.excl(flRead) # remove a member
echo permissions.contains(flWrite) # true
echo flExec in permissions # false — 'in' is the membership test
# Set algebra works the way mathematics does.
let cpu = {flRead, flExec}
let both = permissions + cpu # union
let shared = permissions * cpu # intersection
echo both.len, " ", shared.len # 2 1
# char sets are the classic use: validating input without a chain of comparisons.
const vowels = {'a', 'e', 'i', 'o', 'u'}
echo 'i' in vowels, " ", 'z' in vowels # true false
Hash Tables and Hash Sets
import std/tables
var stock = initTable[string, int]() # key: string, value: int
stock["apples"] = 3 # '[]=' inserts or overwrites
stock["pears"] = 5
echo stock.len, " kinds" # 2
echo stock.getOrDefault("plums", 0) # 0 — a missing key returns the default
if stock.hasKey("apples"): # test before reading when absence matters
stock["apples"] = stock["apples"] - 1 # no KeyError: the key is known to exist
for name, count in stock.pairs: # pairs walks keys and values together
echo name, " = ", count
# Table keys use '==' and 'hash'; define hash() in std/hashes for custom keys.
stock.clear()
echo stock.len # 0
Iteration Helpers and Algorithms
Two modules remove most manual loops: std/algorithm for ordering and searching, and std/sequtils for transforming sequences into new sequences.
Sorting and Searching
import std/algorithm
var scores = @[42, 7, 19, 42, 3]
scores.sort() # ascending, in place
echo scores # @[3, 7, 19, 42, 42]
scores.reverse() # also in place
echo scores # @[42, 42, 19, 7, 3]
echo scores.binarySearch(19) # index of 19 in the sorted sequence
# A custom comparator sorts by any rule you can express as a bool.
var names = @["Grace", "Ada", "Linus"]
names.sort(proc (a, b: string): int = cmp(a.len, b.len)) # cmp: -1, 0, 1
echo names # @["Ada", "Linus", "Grace"]
Transforming with sequtils
import std/sequtils
let values = @[1, 2, 3, 4, 5]
echo values.mapIt(it * it) # @[1, 4, 9, 16, 25]
echo values.filterIt(it mod 2 == 1) # @[1, 3, 5] — keep odd numbers
echo values.foldl(a + b) # 15 — reduce with '+'
# Mutating in place: 'mitems' yields mutable references into the sequence.
var buffer = @[1, 2, 3]
for item in buffer.mitems:
item *= 10 # writes back into the sequence
echo buffer # @[10, 20, 30]
# zip pairs two sequences; walking both with one loop keeps indices in sync.
echo zip(@["a", "b"], @[1, 2]) # @[("a", 1), ("b", 2)]
Practice
Read a list of words into a seq[string], build a Table[string, int] that counts how often each appears, then print the five most frequent by sorting the pairs with a custom comparator. That single exercise touches sequences, tables, iteration and algorithms — continue with Strings & Text.