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)
}

Leave a Comment

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