Gleam Maps
A map stores key-value pairs. Each key maps to exactly one value. Maps let you look up a value by name instead of by position — think of a map as a dictionary where you look up a word (key) to find its definition (value).
Creating a Map
import gleam/map
let scores = map.from_list([
#("Alice", 95),
#("Bob", 82),
#("Carol", 91)
])
Map Visualization
──────────────────────────────────────────────────
┌─────────┬───────┐
│ Key │ Value │
├─────────┼───────┤
│ "Alice" │ 95 │
│ "Bob" │ 82 │
│ "Carol" │ 91 │
└─────────┴───────┘
Looking Up a Value
let result = map.get(scores, "Bob")
// Ok(82)
let missing = map.get(scores, "David")
// Error(Nil) — key does not exist
map.get returns a Result because the key might not exist. Gleam forces you to handle both cases — success and absence — through pattern matching.
Inserting and Updating
let updated = map.insert(scores, "David", 88)
// Adds "David" -> 88
let corrected = map.insert(scores, "Bob", 85)
// Replaces Bob's old value (82) with 85
Because maps are immutable, map.insert returns a new map. The original scores map is unchanged.
Deleting a Key
let without_bob = map.delete(scores, "Bob")
// Map now has Alice and Carol only
Checking Keys and Size
let count = map.size(scores) // 3
let has_alice = map.has_key(scores, "Alice") // True
let all_keys = map.keys(scores) // ["Alice", "Bob", "Carol"]
let all_vals = map.values(scores) // [95, 82, 91]
Iterating Over a Map
let pairs = map.to_list(scores)
// [#("Alice", 95), #("Bob", 82), #("Carol", 91)]
let mapped = map.map_values(scores, fn(_, v) { v + 5 })
// Adds 5 to every score
Map Types
The type of a map is Map(KeyType, ValueType):
let phone_book: Map(String, String) = map.from_list([
#("Alice", "+91-99999-00001"),
#("Bob", "+91-99999-00002")
])
Practical Example — Word Counter
import gleam/map
import gleam/list
import gleam/string
import gleam/io
pub fn count_words(text: String) -> Map(String, Int) {
let words = string.split(text, " ")
list.fold(words, map.new(), fn(counts, word) {
let current = map.get(counts, word) |> result.unwrap(0)
map.insert(counts, word, current + 1)
})
}
pub fn main() {
let freq = count_words("the cat sat on the mat the cat")
io.debug(map.get(freq, "the")) // Ok(3)
io.debug(map.get(freq, "cat")) // Ok(2)
}
Word Count Map
──────────────────────────────────────────────────
"the" → 3
"cat" → 2
"sat" → 1
"on" → 1
"mat" → 1
Key Points
Map Essentials
──────────────────────────────────────────────────
1. Create with map.from_list([#(key, value), ...])
2. Look up with map.get → returns Result
3. Insert and update with map.insert
4. Remove with map.delete
5. Immutable — all operations return new maps
6. Type: Map(KeyType, ValueType)
7. Keys must support equality (most types do)
Maps excel when you need to look up data by a meaningful name rather than an index. Use them for configuration, frequency counting, caching computed results, and building lookup tables.
Merging Two Maps
import gleam/map
let defaults = map.from_list([
#("timeout", "30"),
#("retries", "3"),
#("debug", "false")
])
let overrides = map.from_list([
#("debug", "true"),
#("host", "localhost")
])
let merged = map.merge(defaults, overrides)
// timeout → "30" (from defaults, not overridden)
// retries → "3" (from defaults)
// debug → "true" (overridden)
// host → "localhost" (new from overrides)
map.merge Visual
──────────────────────────────────────────────────
defaults: {timeout:30, retries:3, debug:false}
overrides: {debug:true, host:localhost}
│
▼ merge (right wins on conflict)
merged: {timeout:30, retries:3, debug:true, host:localhost}
Transforming Map Values
let prices = map.from_list([
#("apple", 100),
#("banana", 60),
#("mango", 150)
])
// Apply 10% discount to all prices
let discounted = map.map_values(prices, fn(_, price) { price - price / 10 })
// apple → 90, banana → 54, mango → 135
Filtering Map Entries
let inventory = map.from_list([
#("pen", 0),
#("book", 12),
#("eraser", 0),
#("ruler", 5)
])
let in_stock = map.filter(inventory, fn(_, qty) { qty > 0 })
// {"book": 12, "ruler": 5}
filter Visual
──────────────────────────────────────────────────
{pen:0, book:12, eraser:0, ruler:5}
│
keep only qty > 0
│
{book:12, ruler:5}
Folding Over a Map
let cart = map.from_list([
#("shirt", 800),
#("jeans", 1500),
#("cap", 400)
])
let total = map.fold(cart, 0, fn(acc, _key, value) { acc + value })
// 2700
Maps with Custom Key Types
type OrderStatus { Pending; Processing; Shipped; Delivered }
let counts = map.from_list([
#(Pending, 15),
#(Processing, 8),
#(Shipped, 42),
#(Delivered, 103)
])
let delivered_count = map.get(counts, Delivered)
// Ok(103)
Practical Example — Inventory Manager
import gleam/map
import gleam/io
import gleam/list
type Inventory = Map(String, Int)
pub fn restock(inv: Inventory, item: String, qty: Int) -> Inventory {
let current = map.get(inv, item) |> result.unwrap(0)
map.insert(inv, item, current + qty)
}
pub fn sell(inv: Inventory, item: String, qty: Int) -> Result(Inventory, String) {
case map.get(inv, item) {
Error(_) -> Error("Item not found: " <> item)
Ok(stock) ->
case stock >= qty {
False -> Error("Not enough stock for: " <> item)
True -> Ok(map.insert(inv, item, stock - qty))
}
}
}
pub fn low_stock(inv: Inventory, threshold: Int) -> List(String) {
inv
|> map.filter(fn(_, qty) { qty <= threshold })
|> map.keys
}
pub fn main() {
let inv = map.new()
let inv = restock(inv, "pen", 100)
let inv = restock(inv, "book", 50)
case sell(inv, "pen", 30) {
Ok(inv2) -> io.debug(map.get(inv2, "pen")) // Ok(70)
Error(e) -> io.println(e)
}
io.debug(low_stock(inv, 60))
// ["book"] (50 <= 60 threshold)
}
