--- layout: page published: true noToc: false noComments: false noDate: false title: Fun - Maps in Fun subtitle: Working with maps, construction, lookup/update, merging, iteration, and common patterns. description: Working with maps, construction, lookup/update, merging, iteration, and common patterns. permalink: /documentation/maps/ lang: en tags: - common - construction - iteration - lookup - maps - merging - patterns - update --- This guide focuses on maps: creation, reading/writing by key, checking key presence, iterating keys/values, nesting, and common gotchas. It complements the quick overview in types.md with a deeper, example‑driven treatment. ## What is a map? - Associative container of key → value pairs. - Keys are typically strings; values can be any type (numbers, strings, arrays, maps, …). - Mutable: assigning with m["k"] = v updates the map in place. - Accessing a missing key yields nil (not an error). Use guards or has(m, key). - Maps are not ordered; rely on keys(m) if you need a concrete list of keys. ## Creating maps ``` // literals user = { "name": "Ada", "age": 37 } empty = {} print(typeof(user)) // "map" ``` Nested structures are natural and common: ``` book = { "title": "Fun Handbook", "meta": { "pages": 120, "isbn": "123-456" }, "tags": ["lang", "vm"] } print(book["meta"]["pages"]) // 120 ``` ## Getting and setting by key ``` profile = { "name": "Lin" } // read existing key print(profile["name"]) // Lin // read missing key → nil print(profile["email"]) // nil // write / overwrite profile["email"] = "lin@example.org" profile["name"] = "Linus" print(profile) // {"name":"Linus","email":"lin@example.org"} ``` Notes: - Using a non‑string key is allowed only if your build/runtime supports it; most code uses string keys for portability. - Missing keys produce nil. Compare against nil before converting or indexing: ``` v = profile["phone"] if v == nil { print("no phone on file") } ``` ## Checking key existence Use has(m, key) to check if a key is present (returns 1 or 0): ``` cfg = { "debug": 1 } print(has(cfg, "debug")) // 1 print(has(cfg, "port")) // 0 if has(cfg, "port") { print(cfg["port"]) } else { print("using default port") } ``` ## Iterating maps Maps are not inherently ordered. To iterate, first obtain an array of keys or values. ``` user = { "name": "Ada", "age": 38 } // iterate known keys (explicit order you choose) order = ["name", "age"] for i = 0; i < len(order); i = i + 1 { k = order[i] print(k + " = " + to_string(user[k])) } // discover keys from the map (order may depend on implementation) ks = keys(user) // -> ["name", "age"] (example) for i = 0; i < len(ks); i = i + 1 { k = ks[i] print(k + ": " + to_string(user[k])) } // values only vs = values(user) // -> ["Ada", 38] for i = 0; i < len(vs); i = i + 1 { print(to_string(vs[i])) } ``` Tip: - If you need deterministic output, either define the order array explicitly or sort the result of keys(user) using your available utilities before looping. ## Copying vs. referencing Maps are reference types. Assigning copies the reference, not the contents: ``` orig = { "a": 1 } alias = orig alias["a"] = 9 print(orig["a"]) // 9 // To make a shallow copy, rebuild from keys/values src = { "x": 1, "y": 2 } dst = {} ks = keys(src) for i = 0; i < len(ks); i = i + 1 { k = ks[i]; dst[k] = src[k] } dst["x"] = 7 print(src["x"]) // 1 print(dst["x"]) // 7 ``` Shallow copies duplicate only the top‑level mapping; nested arrays/maps inside are still shared unless you clone them manually. ## Equality ``` print({"a":1,"b":2} == {"b":2,"a":1}) // true print({"a":1} == {"a":2}) // false ``` Map equality compares sets of keys and their corresponding values for equality (order does not matter). ## Common utilities Depending on your build/stdlib configuration, these helpers are commonly available: - has(m, key): 1 if present, 0 otherwise - keys(m): array of keys - values(m): array of values (parallel to keys(m) order) - len(a) works on arrays; for maps, prefer keys(m) then len(keys(m)) if you need a count Check your lib or VM docs (e.g., src/vm/maps) and documentation/types.md for availability and details. ## Interop with arrays and strings ``` // maps inside arrays users = [ {"name":"Ada"}, {"name":"Lin"} ] for i = 0; i < len(users); i = i + 1 { print(users[i]["name"]) // Ada, Lin } // arrays inside maps m = { "nums": [1,2,3] } arr = m["nums"] push(arr, 4) print(m["nums"]) // [1,2,3,4] // JSON interop is typically via lib/io/json.fun (if enabled in your build) #include as json // adjust to your tree and build flags s = json.stringify({"ok":1}) // "{"ok":1}" ``` ## Error handling and edge cases - Accessing a missing key returns nil. Guard before arithmetic or nested indexing. - Mutating a map while iterating over keys you computed earlier is safe, but remember the keys array won’t update automatically; recompute if needed. - Treat map iteration order as unspecified unless your build guarantees stability. For user‑facing output, specify/derive an order explicitly. ## Performance tips - If you will access many keys, cache ks = keys(m) once and index that array in a loop rather than calling keys(m) repeatedly. - Prefer direct writes (m["k"]=v) over repeatedly rebuilding maps in tight loops. - When copying, copy only required keys instead of cloning the entire map if you only need a subset. ## Examples ``` // merge defaults into config (without overwriting explicit keys) defaults = { "host":"127.0.0.1", "port":8080, "debug":0 } cfg = { "port": 9000 } ks = keys(defaults) for i = 0; i < len(ks); i = i + 1 { k = ks[i] if !has(cfg, k) { cfg[k] = defaults[k] } } print(cfg) // {"port":9000,"host":"127.0.0.1","debug":0} // index users by id rows = [ {"id":"u1","name":"Ada"}, {"id":"u2","name":"Lin"} ] by_id = {} for i = 0; i < len(rows); i = i + 1 { r = rows[i] by_id[r["id"]] = r } print(by_id["u2"]["name"]) // Lin ``` ## See also - types.md — broader overview of core types with quick map examples. - examples/ — there are example scripts involving maps; try play.fun to explore.