# Maps

> brine maps ({K: V}) are hash maps that remember insertion order: reading, inserting, removing, iterating, allowed key types and value semantics.

Source: https://docs.brinelang.org/language/maps/

A map `{K: V}` stores values by key and remembers the order keys were first inserted. Reading a key that isn’t there **panics**, so test with `in` or use `m.get(k)`, which returns an `Option`. Like arrays, maps are values: `let b = a` copies.

```brine
fn main():
    var ages = {"ann": 31, "bob": 27}
    ages["cy"] = 40
    ages["ann"] += 1
    print(ages, len(ages))
    if "bob" in ages:
        print(ages["bob"])
    print(ages.get("zed"), ages.get("zed", 0))
```

Output

```text
{"ann": 32, "bob": 27, "cy": 40} 3
27
None 0
```

## Making maps

[Section titled “Making maps”](#making-maps)

Write `key: value` pairs in braces. The key and value types are inferred from the first pair. An empty map needs a type annotation:

```brine
fn main():
    let squares = {1: 1, 2: 4, 3: 9}
    var counts: {str: int} = {}
    for w in ["a", "b", "a", "c", "a"]:
        if w in counts:
            counts[w] += 1
        else:
            counts[w] = 1
    print(squares, counts)
```

Output

```text
{1: 1, 2: 4, 3: 9} {"a": 3, "b": 1, "c": 1}
```

`let m = {}` with no type is error [E0308](/reference/errors/#e0308). A map type can appear anywhere a type can: `fn count(words: [str]) -> {str: int}`, a struct field, a map of arrays `{str: [int]}`.

## Reading and writing

[Section titled “Reading and writing”](#reading-and-writing)

* `m[k]` reads the value for `k`. A missing key panics.
* `m[k] = v` inserts `k`, or replaces its value if it’s already there. Replacing keeps the key’s original position.
* `m[k] += 1` and the other compound assignments change a value in place. The key must exist.
* `k in m` and `k not in m` test for a key.
* `m.get(k)` returns `Some(value)` or `None`; `m.get(k, default)` returns the value or `default`.
* `m.remove(k)` removes `k` and returns `true` if it was there.

```brine
fn main():
    var m: {str: int} = {}
    m["z"] = 1
    m["a"] = 2
    m["z"] = 3
    print(m)
    let first = m.remove("z")
    let second = m.remove("z")
    print(first, second, m)
    m["z"] = 4
    print(m)
```

Output

```text
{"z": 3, "a": 2}
true false {"a": 2}
{"a": 2, "z": 4}
```

A key that was removed and inserted again goes to the end.

### Missing keys

[Section titled “Missing keys”](#missing-keys)

```brine
fn main():
    let ages = {"ann": 31}
    print(ages["zed"])
```

Output

```text
panic: key "zed" is not in the map
  --> main.br:3:11
```

A missing key usually means a bug or bad input. When it’s expected, say so with `get`:

```brine
fn main():
    let prices = {"apple": 3, "pear": 4}
    let order = ["apple", "kiwi", "pear"]
    var total = 0
    for item in order:
        match prices.get(item):
            case Some(p):
                total += p
            case None:
                print("no price for", item)
    print(total, prices.get("kiwi", 0))
```

Output

```text
no price for kiwi
7 0
```

## Looping over a map

[Section titled “Looping over a map”](#looping-over-a-map)

`for k in m` walks the keys in insertion order. `m.items()` gives `(key, value)` pairs to unpack:

```brine
fn main():
    var stock = {"apple": 3, "pear": 0, "fig": 7}
    for name in stock:
        if stock[name] == 0:
            stock.remove(name)
    for name, n in stock.items():
        print(name, n)
    print(stock.keys(), stock.values(), stock.items())
```

Output

```text
apple 3
fig 7
["apple", "fig"] [3, 7] [("apple", 3), ("fig", 7)]
```

The loop walks a snapshot of the map taken when it starts, so the body may insert and remove keys. Keys added in the body don’t show up in that loop.

`keys()`, `values()` and `items()` return new arrays, in insertion order.

## Key types

[Section titled “Key types”](#key-types)

Keys can be integers, `bool`, `str`, enums, and arrays, tuples and structs made of those. Floats can’t be keys:

```brine
struct Cell:
    row: int
    col: int


enum Color:
    Red
    Green


fn main():
    var grid: {(int, int): str} = {}
    grid[(0, 1)] = "x"
    let cells = {Cell(row=1, col=2): 5}
    let lists = {Color.Red: [1, 2], Color.Green: []}
    print(grid, cells[Cell(row=1, col=2)], lists[Color.Red])
```

Output

```text
{(0, 1): "x"} 5 [1, 2]
```

```brine
fn main():
    let m: {float: str} = {}
```

```text
error[E0335]: `float` can't be used as a map key
 --> main.br:2:12
  |
2 |     let m: {float: str} = {}
  |            ^^^^^^^^^^^^ invalid key type
  |
  = note: map keys must be integers, `bool`, `str`, enums, or arrays, tuples and structs of those
  = help: floats aren't allowed because `nan != nan`; use an integer key instead
  = run `brine explain E0335` for more about this error
error: could not compile due to 1 error
```

A key must be equal to itself for lookups to work, and `nan` isn’t. To key by a measurement, round it to an integer first (for example, millimetres instead of metres).

## Maps are values

[Section titled “Maps are values”](#maps-are-values)

Assigning a map or passing it to a function copies it. The copy is lazy (copy-on-write), so it costs nothing until one side changes:

```brine
fn main():
    var a = {"x": 1}
    let b = a
    a["x"] = 2
    a["y"] = 3
    print(a, b)
    print({"p": 1, "q": 2} == {"q": 2, "p": 1})
```

Output

```text
{"x": 2, "y": 3} {"x": 1}
true
```

`==` compares keys and values and ignores order. To let a function change the caller’s map, take it as `ref`: `fn add(ref m: {str: int}, k: str)`. See [functions](/language/functions/).

`par` bodies may read maps but not change outer ones. See [parallelism](/parallelism/overview/).

## Reference

[Section titled “Reference”](#reference)

| Operation              | Result      | Notes                                     |
| ---------------------- | ----------- | ----------------------------------------- |
| `{k1: v1, k2: v2}`     | `{K: V}`    | empty: `var m: {K: V} = {}`               |
| `len(m)`               | `int`       | number of keys                            |
| `m[k]`                 | `V`         | panics if `k` is missing                  |
| `m[k] = v`             |             | insert or replace                         |
| `k in m`, `k not in m` | `bool`      |                                           |
| `m.get(k)`             | `Option[V]` |                                           |
| `m.get(k, default)`    | `V`         |                                           |
| `m.remove(k)`          | `bool`      | `true` if `k` was there                   |
| `m.keys()`             | `[K]`       | insertion order                           |
| `m.values()`           | `[V]`       | insertion order                           |
| `m.items()`            | `[(K, V)]`  | insertion order                           |
| `for k in m`           |             | keys, in insertion order, over a snapshot |
| `m == n`               | `bool`      | same keys and values, any order           |

## Rules

[Section titled “Rules”](#rules)

* Maps remember insertion order; replacing a value keeps the key’s place.
* `m[k]` panics on a missing key. Use `in` or `get` when a key may be absent.
* Keys can’t be floats, or contain floats.
* `let b = a` copies; changing `b` never changes `a`.
* A loop over a map sees a snapshot, so the body may change the map.

Related: [arrays](/language/arrays/), [strings](/language/strings/), [generics](/language/generics/) (maps work with type parameters: `fn invert[K, V](m: {K: V}) -> {V: K}`), [error handling](/language/error-handling/).
