Gleam Dict
The gleam/dict module is the current standard API for key-value stores in Gleam, replacing the older gleam/map. A Dict stores an unordered collection of key-value pairs where each key is unique. It is the go-to data structure whenever you need to look something up by name rather than by position.
Dict vs Map
gleam/map → older API, still works but being phased out
gleam/dict → current standard, use this in new code
The Dict type is:
import gleam/dict
Dict(KeyType, ValueType)
Creating a Dict
import gleam/dict
// From a list of pairs
let scores = dict.from_list([
#("Alice", 95),
#("Bob", 82),
#("Carol", 91)
])
// Empty dict
let empty = dict.new()
// Single entry
let one = dict.from_list([#("key", "value")])
Dict Visualization
──────────────────────────────────────────────────
scores = dict.from_list([#("Alice",95), #("Bob",82), #("Carol",91)])
┌─────────┬───────┐
│ Key │ Value │
├─────────┼───────┤
│ "Alice" │ 95 │
│ "Bob" │ 82 │
│ "Carol" │ 91 │
└─────────┴───────┘
Reading Values
let result = dict.get(scores, "Bob")
// Ok(82)
let missing = dict.get(scores, "Dave")
// Error(Nil)
dict.get always returns a Result because the key might not exist. The compiler forces you to handle both cases.
Inserting and Updating
// Insert a new key
let with_dave = dict.insert(scores, "Dave", 88)
// Update an existing key (same function — insert replaces)
let corrected = dict.insert(scores, "Bob", 85)
// Update using current value
let bumped = dict.upsert(scores, "Alice", fn(existing) {
case existing {
Some(s) -> s + 5 // Alice exists — add 5 bonus points
None -> 50 // Alice missing — start at 50
}
})
upsert Pattern
──────────────────────────────────────────────────
Key exists → fn(Some(old_value)) → new value
Key missing → fn(None) → initial value
Useful for: counters, accumulating lists, safe defaults
Removing Entries
let without_bob = dict.delete(scores, "Bob")
// scores unchanged; without_bob has Alice and Carol only
Querying the Dict
dict.size(scores) // 3
dict.has_key(scores, "Alice") // True
dict.has_key(scores, "Dave") // False
dict.is_empty(empty) // True
dict.keys(scores) // ["Alice", "Bob", "Carol"] (any order)
dict.values(scores) // [95, 82, 91] (any order)
dict.to_list(scores) // [#("Alice",95), #("Bob",82), #("Carol",91)]
Transforming a Dict
// Transform every value
let doubled = dict.map_values(scores, fn(_key, v) { v * 2 })
// Alice→190, Bob→164, Carol→182
// Keep only entries matching a condition
let high_scorers = dict.filter(scores, fn(_key, v) { v >= 90 })
// Alice→95, Carol→91
// Reduce to a single value
let total = dict.fold(scores, 0, fn(acc, _key, v) { acc + v })
// 268
filter Visual
──────────────────────────────────────────────────
{Alice:95, Bob:82, Carol:91}
│
keep only score >= 90
│
{Alice:95, Carol:91}
Merging Two Dicts
let defaults = dict.from_list([
#("timeout", 30),
#("retries", 3),
#("debug", 0)
])
let config = dict.from_list([
#("debug", 1),
#("port", 8080)
])
let merged = dict.merge(defaults, config)
// timeout → 30 (from defaults)
// retries → 3 (from defaults)
// debug → 1 (config wins on conflict)
// port → 8080 (new from config)
Dict Type Signature
Type Notation
──────────────────────────────────────────────────
Dict(String, Int) → string keys, int values
Dict(Int, User) → int keys, User record values
Dict(ProductId, Quantity) → custom type keys and values
pub fn find_product(db: Dict(Int, Product), id: Int) -> Option(Product) {
dict.get(db, id) |> option.from_result
}
Practical Example — Frequency Counter
import gleam/dict
import gleam/list
import gleam/string
import gleam/io
pub fn word_frequencies(text: String) -> Dict(String, Int) {
text
|> string.lowercase
|> string.split(" ")
|> list.filter(fn(w) { string.length(w) > 0 })
|> list.fold(dict.new(), fn(freq, word) {
dict.upsert(freq, word, fn(current) {
case current {
Some(n) -> n + 1
None -> 1
}
})
})
}
pub fn top_words(freq: Dict(String, Int), n: Int) -> List(#(String, Int)) {
freq
|> dict.to_list
|> list.sort(fn(a, b) {
case a.1 > b.1 { True -> order.Lt; False -> order.Gt }
})
|> list.take(n)
}
pub fn main() {
let text = "the cat sat on the mat the cat"
let freq = word_frequencies(text)
io.debug(top_words(freq, 3))
// [#("the", 3), #("cat", 2), #("sat", 1)]
}
Dict vs List — When to Choose
Decision Guide
──────────────────────────────────────────────────
Use List when:
✓ Order matters
✓ You process every element
✓ No lookup by key needed
✓ Small collections (< 20 items)
Use Dict when:
✓ You look up by a unique key
✓ You update individual entries by key
✓ Order does not matter
✓ Fast lookup matters (O(log n) vs O(n))
Key Points
Dict Essentials
──────────────────────────────────────────────────
1. import gleam/dict — use this, not gleam/map
2. dict.from_list([#(k,v), ...]) creates a Dict
3. dict.get(d, key) → Result (always handle Error)
4. dict.insert(d, key, value) → new dict
5. dict.upsert(d, key, fn(Option)) → update safely
6. dict.delete, dict.merge, dict.filter available
7. dict.map_values and dict.fold for transformation
8. Immutable — all operations return new dicts
The Dict type is your primary tool for keyed data in Gleam. Its functional API — every operation returns a new dict, nothing mutates — makes dict-based code predictable and easy to test. Whenever you reach for a dictionary, hash map, or lookup table, gleam/dict is the right module.
