Skip to content

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.

01-new-and-set-get.zolo
Playground
// 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.

02-has-and-remove.zolo
Playground
// 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.

03-keys-values-entries.zolo
Playground
// 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.

04-iteration.zolo
Playground
// 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.

05-literal-vs-procedural.zolo
Playground
// 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.

enespt-br