Maps (std::map)
A Map in Zolo is a collection of key–value pairs with no guaranteed order.
Import with use std::Map. There are two ways to create maps: the #{} literal
for static data and the Map::new() API for dynamic construction.
Create, insert and read
Map::new() creates an empty map. set(k, v) inserts or overwrites; get(k)
returns the value or nil if the key does not exist.
Empty creation, mixed-type insertion, reading, missing key, overwrite and len.
// Feature: Map.new / Map.set / Map.get — create and populate an empty map
// When to use: building a map dynamically, without a fixed-size literal.
use std::Map
// Create an empty map.
let m = Map::new()
// Insert key/value pairs.
m.set("name", "Zolo")
m.set("version", "0.1.0")
m.set("stable", false)
// Retrieve.
print(m.get("name")) // expected: Zolo
print(m.get("version")) // expected: 0.1.0
print(m.get("stable")) // expected: false
// Missing key returns nil.
let missing = m.get("author")
print(missing) // expected: nil
// Overwrite — `set` on an existing key updates it.
m.set("version", "0.2.0")
print(m.get("version")) // expected: 0.2.0
// Size.
print(m.len()) // expected: 3
Check and remove
has(k) tests presence without consuming the value; remove(k) deletes the key —
operating on a missing key is safe.
Word count with has/set in a loop; idempotent remove.
// Feature: Map.has / Map.remove — testing and removing keys
// When to use: invalidating entries, counting before mutating, avoiding nil.
use std::Map
let cfg = Map::new()
cfg.set("host", "localhost")
cfg.set("port", "8080")
cfg.set("debug", "true")
// has — true/false.
print(cfg.has("host")) // expected: true
print(cfg.has("missing")) // expected: false
// remove — deletes the key.
cfg.remove("debug")
print(cfg.has("debug")) // expected: false
print(cfg.len()) // expected: 2
// remove on a missing key is safe (no-op).
cfg.remove("no-such-key")
print(cfg.len()) // expected: 2
// Pattern: increment a counter while checking for existence.
let counter = Map::new()
let words = ["foo", "bar", "foo", "baz", "foo"]
for w in words {
if counter.has(w) {
counter.set(w, counter.get(w) + 1)
} else {
counter.set(w, 1)
}
}
print(counter.get("foo")) // expected: 3
print(counter.get("bar")) // expected: 1
Keys, values and entries
keys(), values() and entries() return arrays. Use Map::from(#{...})
to wrap a literal and access these methods.
Sum of values via reduce; filtering keys by initial letter with filter.
// Feature: Map.keys / Map.values / Map.entries — extracting contents
// When to use: serializing, counting, transforming the whole map at once.
use std::Map
use std::Array
// `#{...}` is a plain map literal — build a Map object with Map.from.
let scores = Map::from(#{math: 95, science: 87, english: 92})
// keys — array with the keys (order not guaranteed).
let keys = scores.keys()
print(keys.len()) // expected: 3
// values — array with the values.
let vals = scores.values()
print(vals.len()) // expected: 3
// entries — array of [key, value] pairs.
let pairs = scores.entries()
print(pairs.len()) // expected: 3
// Each entry is [k, v]. Check a known entry.
print(scores.get("math")) // expected: 95
// Sum the values via reduce.
let total = scores.values().reduce(|acc, x| acc + x, 0)
print(total) // expected: 274
// Number of keys starting with a vowel.
let starts_vowel = scores.keys().filter(|k| {
let c = k.chars()[0]
return c == "a" || c == "e" || c == "i" || c == "o" || c == "u"
})
print(starts_vowel.len()) // expected: 1
Iteration
There are three equivalent ways to iterate over a map: for k in m.keys(),
m.each(|k, v| ...) and for entry in m.entries().
The three iteration forms side by side; key order is not guaranteed.
// Feature: Iterating over a Map — `for` over keys, or Map.each
// When to use: walking through all pairs to print, validate, etc.
use std::Map
// `#{...}` is a plain table literal — wrap with Map.from to use Map.*.
let user = Map::from(#{name: "Alice", age: 30, active: true})
// Form 1: iterate through the keys and index in.
for k in user.keys() {
let v = user.get(k)
print("{k} = {v}")
}
// (key order is not guaranteed)
print("---")
// Form 2: m.each(fn) — receives k, v.
user.each(|k, v| print("each: {k} = {v}"))
print("---")
// Form 3: iterate through entries (array of pairs).
for entry in user.entries() {
let k = entry[0]
let v = entry[1]
print("entry: {k} = {v}")
}
#{} literal versus procedural API
The #{key: value} literal is the shorthand for fixed data. Prefer Map::new()
when keys or values come from variables at runtime. To mix both, wrap the literal
with Map::from.
Dot access (.name) and bracket access; non-identifier keys ("user-id"); Map::from for post-literal mutation.
// Feature: Map literal `#{...}` vs procedural API `Map::new() + Map.set`
// When to use: literal for known data, API for dynamic construction.
use std::Map
// ── Literal — concise, ideal for static data ─────────────────
let user = #{name: "Alice", age: 30, active: true}
print(user["name"]) // expected: Alice
print(user["age"]) // expected: 30
// Dot access when the key is a valid identifier.
print(user.name) // expected: Alice
print(user.active) // expected: true
// Bracket always works — required for non-identifier keys.
let labels = #{"user-id": 42, "x-trace": "abc"}
print(labels["user-id"]) // expected: 42
print(labels["x-trace"]) // expected: abc
// ── Procedural — required for runtime construction ───────────
let m = Map::new()
let names = ["alice", "bob", "carol"]
for name in names {
m.set(name, name.len())
}
print(m.get("alice")) // expected: 5
print(m.get("carol")) // expected: 5
print(m.len()) // expected: 3
// ── Mix — start from a literal and mutate later ──────────────
// Wrap the literal with Map.from so the Map.* helpers (which expect
// the Map shape with `__data` / `__size`) keep working.
let cfg = Map::from(#{host: "localhost", port: 8080})
cfg.set("debug", true)
print(cfg.has("debug")) // expected: true
print(cfg.len()) // expected: 3
Challenge
Given an array of strings, build a {word: length} map using
Map::new() and a for loop. Then use keys() and filter to list only
words with length greater than 4.
See also