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.

Leave a Comment

Your email address will not be published. Required fields are marked *