# Hashes and dictionaries

> Store keyed data, handle missing keys, and transform dictionaries with key-value blocks.

Canonical: https://alice-lang.org/documentation/library/hashes/

[Documentation index](https://alice-lang.org/documentation/llms.txt) · [Complete book](https://alice-lang.org/llms-full.txt)

## Create and update

A hash is a .NET dictionary. Literal syntax infers the key and value types, while `Hash(K, V).new` creates an empty typed dictionary.

```alice
scores = {"Alice" => 3, "Rabbit" => 5}
scores["Alice"] += 2
puts scores["Alice"]
puts scores.key?("Hatter")
puts scores.values.sum
```

Expected output:

```text
5
false
10
```

[Edit and run in the Alice playground](https://playground.alice-lang.org/#code=u.c2NvcmVzID0geyJBbGljZSIgPT4gMywgIlJhYmJpdCIgPT4gNX0Kc2NvcmVzWyJBbGljZSJdICs9IDIKcHV0cyBzY29yZXNbIkFsaWNlIl0KcHV0cyBzY29yZXMua2V5PygiSGF0dGVyIikKcHV0cyBzY29yZXMudmFsdWVzLnN1bQ)

Index assignment inserts or replaces a key's value. Index reads require the key to exist. `scores["missing"] += 1` does not initialize a counter; it reads first and throws if the key is absent.

## Missing keys

| Operation                         | Meaning                                                |
| --------------------------------- | ------------------------------------------------------ |
| `hash.key?(key)`, `has_key?(key)` | Test for a key                                         |
| `hash.value?(value)`              | Test for a value                                       |
| `hash.fetch(key)`                 | Read or throw for a missing key                        |
| `hash.fetch(key, fallback)`       | Read or return a fallback                              |
| `hash.GetValueOrDefault(key)`     | .NET lookup with the value type's default              |
| `hash.dig(key)`                   | Return a reference-type value or nil for a missing key |

For word counts, initialize explicitly or use `GetValueOrDefault`:

```alice
counts = Hash(String, Int32).new
for word in ["tea", "cake", "tea"]
  counts[word] = counts.GetValueOrDefault(word) + 1
end
puts counts["tea"]
```

## Key-value blocks

Hash iterators pass two block arguments, `|key, value|`. `each` and `each_pair` visit entries. `map` creates transformed results, while `select` and `reject` retain dictionary entries based on a predicate.

```alice
prices = {"tea" => 250, "cake" => 400}
prices.each { |name, cents| puts "#{name}: #{cents}" }
```

## Transformations

| Methods                                    | Purpose                                             |
| ------------------------------------------ | --------------------------------------------------- |
| `keys`, `values`                           | Materialize keys or values                          |
| `to_a`, `to_h`                             | Convert supported pair collections                  |
| `merge(other)`                             | Combine dictionary entries                          |
| `invert`                                   | Reverse keys and values; duplicate values need care |
| `transform_values`                         | Map values while retaining keys                     |
| `transform_keys`                           | Map keys; collisions can change results             |
| `sort_by`, `min_by`, `max_by`              | Order or choose entries by a key function           |
| `group_by`, `sum`, `count`, `any?`, `all?` | Aggregate key-value entries                         |

Avoid mutating a dictionary's structure while enumerating it. A normal dictionary is not a concurrent map; use locking, messaging, or .NET concurrent collections for shared writes.

## Continue reading

- Previous: [Arrays, lists, and iterators](https://alice-lang.org/documentation/library/collections.md)
- Next: [Strings and formatting](https://alice-lang.org/documentation/library/strings.md)
