Maps ({"a": 1})
Maps in Luma are key-value collections. They’re the right choice when you want to look things up by a key instead of by index.
Think of them as “dictionaries” or “hash maps” in other languages.
Map Types
Map types use curly braces with a key type and a value type:
{K: V}Some common examples:
{str: int} // map from strings to ints
{int: str} // map from ints to strings
{str: bool} // feature flags by name
{str: Person} // map from username to a Person struct
{str: ?int} // map from string to optional intYou use map types in variable declarations just like any other type:
ages: {str: int}
users: {int: str}
configs: {str: ?int}Quick Reference
| Operation | Syntax | Description |
|---|---|---|
| Declare | ages: {str: int} = {} |
Empty typed map |
| Literal | {"Alice": 30, "Bob": 25} |
Map with initial values |
| Read | ages["Alice"] |
Get value by key (zero value if missing) |
| Write | ages["Alice"] = 31 |
Set or update a key |
| Length | ages.len() |
Number of key-value pairs |
| Walk (k, v) | ages.walk(k, v) -> { ... } |
Iterate over keys and values |
| Walk (v) | ages.walk(v) -> { ... } |
Iterate over values only |
Map Literals
You create a map value using { key: value, ... }:
ages: {str: int} = {
"Alice": 30,
"Bob": 25,
}
prices: {str: float} = {
"apple": 0.99,
"banana": 1.19,
}A few notes:
- Keys and values can be any valid expressions, as long as they match the declared types.
- Trailing commas are allowed and often make diffs nicer.
- An empty map literal is just
{}but you usually want to give the variable a type so the compiler knows key/value types:
counters: {str: int} = {}Reading Values
Index into a map with map[key]:
ages: {str: int} = {
"Alice": 30,
"Bob": 25,
}
print(ages["Alice"]) // 30
print(ages["Bob"]) // 25If a key is missing, you get the zero value for the value type (0 for int, "" for str, false for bool). To distinguish “missing” from “present but zero”, use an optional value type:
scores: {str: ?int} = {}
scores["Alice"] = 42
print(scores["Alice"]) // 42
print(scores["Bob"]) // nil (no score recorded)Updating Maps
Assign to a map entry with map[key] = value:
ages: {str: int} = {
"Alice": 30,
}
ages["Alice"] = 31
ages["Bob"] = 22
print(ages["Alice"]) // 31
print(ages["Bob"]) // 22This works for both updating existing keys and adding new ones.
Length
ages: {str: int} = {"Alice": 30, "Bob": 25}
print(ages.len()) // 2
empty: {str: int} = {}
print(empty.len()) // 0Walking a Map
Use .walk() to iterate over key-value pairs:
ages: {str: int} = {
"Alice": 30,
"Bob": 25,
}
ages.walk(key, value) -> {
print("${key}: ${value}")
}With one parameter, you get the values:
ages.walk(value) -> print(value)
// 30
// 25Maps are unordered — iteration order is not guaranteed.
Use skip to skip an iteration, or stop to exit the walk early:
ages.walk(key, value) -> {
if value < 18 { skip }
print("${key} is an adult")
}See Walk Loops (skip & stop) for more details.
Maps With Structs
struct Person {
name: str
age: int
}
people: {str: Person} = {
"alice": Person("Alice", 30),
"bob": Person("Bob", 25),
}
alice: Person = people["alice"]
print(alice.name) // Alice
print(alice.age) // 30Maps With Optional Values
Optional values are useful when “no value yet” is different from “value is zero”:
last_login: {str: ?int} = {}
user: str = "alice"
if last_login[user] == nil {
print("First login for ${user}")
} else {
print("Welcome back, ${user}")
}Maps With List Values
people_by_city: {str: [str]} = {
"Tallinn": ["Alice", "Bob"],
"Riga": ["Carol"],
}
print(people_by_city["Tallinn"]) // [Alice Bob]
print(people_by_city["Tallinn"][0]) // AliceLists of Maps
A common pattern is a list of maps representing rows of data:
rows: [{str: int}] = [
{"year": 2023, "users": 100},
{"year": 2024, "users": 250},
]
rows.walk(row) -> {
print("Year ${row["year"]}, users: ${row["users"]}")
}Maps and lists compose cleanly and follow the same type syntax:
[T]— list ofT{K: V}— map fromKtoV?T— optionalT
Tips
- Always give maps an explicit type in declarations. This lets the compiler generate typed Go maps instead of a generic
map[interface{}]interface{}.
data = {"a": 1} // generic map (less type safety)
data: {str: int} = { // preferred
"a": 1,
}- Missing keys return the zero value for the value type. If you need to distinguish “missing” from “present”, store an optional value (
?T). - Maps are unordered. Don’t rely on iteration order.