> ## Documentation Index
> Fetch the complete documentation index at: https://companyname-a7d5b98e-ton-storage.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Maps (key-value)

export const Aside = ({type = "note", title = "", icon = "", iconType = "regular", children}) => {
  const asideVariants = ["note", "tip", "caution", "danger"];
  const asideComponents = {
    note: {
      outerStyle: "border-sky-500/20 bg-sky-50/50 dark:border-sky-500/30 dark:bg-sky-500/10",
      innerStyle: "text-sky-900 dark:text-sky-200",
      calloutType: "note",
      icon: <svg width="14" height="14" viewBox="0 0 14 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="w-4 h-4 text-sky-500" aria-label="Note">
          <path fill-rule="evenodd" clip-rule="evenodd" d="M7 1.3C10.14 1.3 12.7 3.86 12.7 7C12.7 10.14 10.14 12.7 7 12.7C5.48908 12.6974 4.0408 12.096 2.97241 11.0276C1.90403 9.9592 1.30264 8.51092 1.3 7C1.3 3.86 3.86 1.3 7 1.3ZM7 0C3.14 0 0 3.14 0 7C0 10.86 3.14 14 7 14C10.86 14 14 10.86 14 7C14 3.14 10.86 0 7 0ZM8 3H6V8H8V3ZM8 9H6V11H8V9Z"></path>
        </svg>
    },
    tip: {
      outerStyle: "border-emerald-500/20 bg-emerald-50/50 dark:border-emerald-500/30 dark:bg-emerald-500/10",
      innerStyle: "text-emerald-900 dark:text-emerald-200",
      calloutType: "tip",
      icon: <svg width="11" height="14" viewBox="0 0 11 14" fill="currentColor" xmlns="http://www.w3.org/2000/svg" className="text-emerald-600 dark:text-emerald-400/80 w-3.5 h-auto" aria-label="Tip">
          <path d="M3.12794 12.4232C3.12794 12.5954 3.1776 12.7634 3.27244 12.907L3.74114 13.6095C3.88471 13.8248 4.21067 14 4.46964 14H6.15606C6.41415 14 6.74017 13.825 6.88373 13.6095L7.3508 12.9073C7.43114 12.7859 7.49705 12.569 7.49705 12.4232L7.50055 11.3513H3.12521L3.12794 12.4232ZM5.31288 0C2.52414 0.00875889 0.5 2.26889 0.5 4.78826C0.5 6.00188 0.949566 7.10829 1.69119 7.95492C2.14321 8.47011 2.84901 9.54727 3.11919 10.4557C3.12005 10.4625 3.12175 10.4698 3.12261 10.4771H7.50342C7.50427 10.4698 7.50598 10.463 7.50684 10.4557C7.77688 9.54727 8.48281 8.47011 8.93484 7.95492C9.67728 7.13181 10.1258 6.02703 10.1258 4.78826C10.1258 2.15486 7.9709 0.000106649 5.31288 0ZM7.94902 7.11267C7.52078 7.60079 6.99082 8.37878 6.6077 9.18794H4.02051C3.63739 8.37878 3.10743 7.60079 2.67947 7.11294C2.11997 6.47551 1.8126 5.63599 1.8126 4.78826C1.8126 3.09829 3.12794 1.31944 5.28827 1.3126C7.2435 1.3126 8.81315 2.88226 8.81315 4.78826C8.81315 5.63599 8.50688 6.47551 7.94902 7.11267ZM4.87534 2.18767C3.66939 2.18767 2.68767 3.16939 2.68767 4.37534C2.68767 4.61719 2.88336 4.81288 3.12521 4.81288C3.36705 4.81288 3.56274 4.61599 3.56274 4.37534C3.56274 3.6515 4.1515 3.06274 4.87534 3.06274C5.11719 3.06274 5.31288 2.86727 5.31288 2.62548C5.31288 2.38369 5.11599 2.18767 4.87534 2.18767Z"></path>
        </svg>
    },
    caution: {
      outerStyle: "border-amber-500/20 bg-amber-50/50 dark:border-amber-500/30 dark:bg-amber-500/10",
      innerStyle: "text-amber-900 dark:text-amber-200",
      calloutType: "warning",
      icon: <svg className="flex-none w-5 h-5 text-amber-400 dark:text-amber-300/80" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" aria-label="Warning">
          <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
        </svg>
    },
    danger: {
      outerStyle: "border-red-500/20 bg-red-50/50 dark:border-red-500/30 dark:bg-red-500/10",
      innerStyle: "text-red-900 dark:text-red-200",
      calloutType: "danger",
      icon: <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="currentColor" className="text-red-600 dark:text-red-400/80 w-4 h-4" aria-label="Danger">
          <path d="M17.1 292c-12.9-22.3-12.9-49.7 0-72L105.4 67.1c12.9-22.3 36.6-36 62.4-36l176.6 0c25.7 0 49.5 13.7 62.4 36L494.9 220c12.9 22.3 12.9 49.7 0 72L406.6 444.9c-12.9 22.3-36.6 36-62.4 36l-176.6 0c-25.7 0-49.5-13.7-62.4-36L17.1 292zm41.6-48c-4.3 7.4-4.3 16.6 0 24l88.3 152.9c4.3 7.4 12.2 12 20.8 12l176.6 0c8.6 0 16.5-4.6 20.8-12L453.4 268c4.3-7.4 4.3-16.6 0-24L365.1 91.1c-4.3-7.4-12.2-12-20.8-12l-176.6 0c-8.6 0-16.5 4.6-20.8 12L58.6 244zM256 128c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"></path>
        </svg>
    }
  };
  let variant = type;
  let gotInvalidVariant = false;
  if (!asideVariants.includes(type)) {
    gotInvalidVariant = true;
    variant = "danger";
  }
  const iconVariants = ["regular", "solid", "light", "thin", "sharp-solid", "duotone", "brands"];
  if (!iconVariants.includes(iconType)) {
    iconType = "regular";
  }
  return <>
      <div className={`callout my-4 px-5 py-4 overflow-hidden rounded-2xl flex gap-3 border ${asideComponents[variant].outerStyle}`} data-callout-type={asideComponents[variant].calloutType}>
        <div className="mt-0.5 w-4" data-component-part="callout-icon">
          {}
          {icon === "" ? asideComponents[variant].icon : <Icon icon={icon} iconType={iconType} size={14} />}
        </div>
        <div className={`text-sm prose min-w-0 w-full ${asideComponents[variant].innerStyle}`} data-component-part="callout-content">
          {gotInvalidVariant ? <p>
              <span className="font-bold">
                Invalid <code>type</code> passed!
              </span>
              <br />
              <span className="font-bold">Received: </span>
              {type}
              <br />
              <span className="font-bold">Expected one of: </span>
              {asideVariants.join(", ")}
            </p> : <>
              {title && <p className="font-bold">{title}</p>}
              {children}
            </>}
        </div>
      </div>
    </>;
};

Tolk supports `map<K, V>` — a high‑level type that encapsulates TVM dictionaries:

* Any serializable keys and values.
* Natural syntax for iterating forwards, backwards, or starting from a specified key.
* Zero overhead compared to low-level approach.

## Create an empty map

```tolk theme={null}
var m: map<int8, int32> = createEmptyMap();
// or
var m = createEmptyMap<int8, int32>();
```

A map is a dedicated type, it may be used in parameters, fields, etc.:

```tolk theme={null}
struct Demo {
    m: map<int64, Point>
}

fun create(): Demo {
    return {
        m: createEmptyMap()
    }
}
```

## Add values to a map

Use `m.set(k, v)` and other methods suggested by an IDE after a dot (a full list is available below):

```tolk theme={null}
var m: map<int8, int32> = createEmptyMap();
m.set(1, 10);
m.addIfNotExists(2, -20);
m.replaceIfExists(2, 20);
m.delete(2);   // now: [ 1 => 10 ]

m.exists(1);   // true
m.exists(2);   // false
```

## Get a value by key

`m.get(key)` returns `isFound + loadValue()`:

```tolk theme={null}
var r = m.get(1);
if (r.isFound) {
    val v = r.loadValue();   // 10
}
```

Note: check **not `r == null`, but `r.isFound`**. In other words, `map.get(key)` returns not `V?`, but a special result.

Also use `m.mustGet(key)` that returns `V` and throws if the key is missing:

```tolk theme={null}
m.mustGet(1);       // 10
m.mustGet(100500);  // runtime error
```

## Iterate forward and backward

There is no dedicated `foreach` syntax. Iteration follows this pattern:

* define the starting key: `r = m.findFirst()` or `r = m.findLast()`
* while `r.isFound`:
  * use `r.getKey()` and `r.loadValue()`
  * move the cursor: `r = m.iterateNext(r)` or `r = m.iteratePrev(r)`

Example: iterate all keys forward

```tolk theme={null}
// suppose there is a map [ 1 => 10, 2 => 20, 3 => 30 ]
// this function will print "1 10 2 20 3 30"
fun iterateAndPrint<K, V>(m: map<K, V>) {
    var r = m.findFirst();
    while (r.isFound) {
        debug.print(r.getKey());
        debug.print(r.loadValue());
        r = m.iterateNext(r);
    }
}
```

Example: iterate backwards from keys ≤ 2

```tolk theme={null}
// suppose `m` is `[ int => address ]` and already populated
// for every key<=2, print addr.workchain
fun printWorkchainsBackwards(m: map<int32, address>) {
    var r = m.findKeyLessOrEqual(2);
    while (r.isFound) {
        val a = r.loadValue();   // it's `address`
        debug.print(a.getWorkchain());
        r = m.iteratePrev(r);
    }
}
```

## Check if a map is empty

```tolk theme={null}
m.isEmpty()    // not `m == null`
```

<Aside type="caution" title={"For experienced readers"}>
  <p>At the TVM level, an empty map is stored as TVM `NULL`.
  But since `map` is a dedicated type, it must be checked with `isEmpty()`.</p>
  <p>Nullable maps <code>{'var m: map<' + '...' + '>?'}</code> are valid,
  then `m` may be null or may hold either an empty map or a non‑empty map.</p>
</Aside>

## Allowed types for K and V

All the following key and value types are valid:

```tolk theme={null}
map<int32, Point?>
map<address, address>
map<Point, map<int3, bool>>
map<uint256, Cell<SnakeData>>
map<bits18, slice>
```

Some types are not allowed. General rules:

* Keys must be fixed-width and contain zero references
  * Valid: `int32`, `address`, `bits256`, `Point`
  * Invalid: `int`, `coins`, `cell`
* Values must be serializable
  * Valid: `coins`, `AnyStruct`, `Cell<AnyStruct>`
  * Invalid: `int`, `builder`

In practice, keys are most commonly `intN`, `uintN`, or `address`. Values can be any serializable type.

## Available methods for maps

An IDE suggests available methods after a dot. Most methods are self-explanatory.

* `createEmptyMap<K, V>(): map<K, V>`

Returns an empty typed map. Equivalent to `PUSHNULL` since TVM `NULL` represents an empty map.

* `createMapFromLowLevelDict<K, V>(d: dict): map<K, V>`

Converts a low-level TVM dictionary to a typed map. Accepts an optional cell and returns the same optional cell.
Mismatched key or value types result in failures when calling `map.get` or related methods.

* `m.toLowLevelDict(): dict`

Converts a high-level map to a low-level TVM dictionary. Returns the same optional cell.

* `m.isEmpty(): bool`

Checks whether a map is empty. Use `m.isEmpty()` instead of `m == null`.

* `m.exists(key: K): bool`

Checks whether a key exists in a map.

* `m.get(key: K): MapLookupResult<V>`

Gets an element by key. Returns `isFound = false` if key does not exist.

* `m.mustGet(key: K, throwIfNotFound: int = 9): V`

Gets an element by key and throws if it does not exist.

* `m.set(key: K, value: V): self`

Sets an element by key. Since it returns `self`, calls may be chained.

* `m.setAndGetPrevious(key: K, value: V): MapLookupResult<V>`

Sets an element and returns the previous element. If no previous element, `isFound = false`.

* `m.replaceIfExists(key: K, value: V): bool`

Sets an element only if the key exists. Returns whether an element was replaced.

* `m.replaceAndGetPrevious(key: K, value: V): MapLookupResult<V>`

Sets an element only if the key exists and returns the previous element.

* `m.addIfNotExists(key: K, value: V): bool`

Sets an element only if the key does not exist. Returns true if added.

* `m.addOrGetExisting(key: K, value: V): MapLookupResult<V>`

Sets an element only if the key does not exist. If exists, returns an old value.

* `m.delete(key: K): bool`

Deletes an element by key. Returns true if deleted.

* `m.deleteAndGetDeleted(key: K): MapLookupResult<V>`

Deletes an element by key and returns the deleted element. If not found, `isFound = false`.

* `m.findFirst(): MapEntry<K, V>`
* `m.findLast(): MapEntry<K, V>`

Finds the first (minimal) or last (maximal) element.
For integer keys, returns minimal (maximal) integer.
For addresses or complex keys (represented as slices), returns lexicographically smallest (largest) key.
Returns `isFound = false` when the map is empty.

* `m.findKeyGreater(pivotKey: K): MapEntry<K, V>`
* `m.findKeyGreaterOrEqual(pivotKey: K): MapEntry<K, V>`
* `m.findKeyLess(pivotKey: K): MapEntry<K, V>`
* `m.findKeyLessOrEqual(pivotKey: K): MapEntry<K, V>`

Finds an element with key compared to pivotKey.

* `m.iterateNext(current: MapEntry<K, V>): MapEntry<K, V>`
* `m.iteratePrev(current: MapEntry<K, V>): MapEntry<K, V>`

Iterates over a map in ascending (descending) order.

## Augmented hashmaps and prefix dictionaries

These structures are rarely used and are not part of the Tolk type system.

* Prefix dictionaries: `import @stdlib/tvm-dicts` and use assembly functions.
* Augmented hashmaps and Merkle proofs: implement interaction manually.

## Keys are auto-serialized

At the TVM level, keys can be numbers or slices. Complex keys, such as `Point`, are automatically serialized and deserialized by the compiler.

```tolk theme={null}
struct Point {
    x: int8
    y: int8
}

fun demo(m: map<Point, V>) {
    // a key is automatically packed to a 16-bit slice
    m.set({x: 10, y: 20}, 123);
    // and unpacked back to `Point`
    return m.findFirst().key;
}
```

If a key is a struct with a single intN field, it behaves like a number.

```tolk theme={null}
struct UserId {
    v: int32
}

struct Demo {
    // equal to K=int32 without extra serialization
    m: map<UserId, V>
}
```

## How to emulate `Set<T>` with maps

Use an "empty tensor" as a type for `V`:

```tolk theme={null}
type Set<T> = map<T, ()>
```

It will work, a bit noisy.
Lots of methods for maps are just inapplicable to sets, so its "public interface" is wrong.
Sets have a much simpler API, literally 4 functions.
It's better to create a simple wrapper:

```tolk theme={null}
struct Set<T> {
    private m: map<T, ()>
}

fun Set<T>.add(self, value: T) { /* ... */ }
// etc.
```

## Low-level: why "isFound" but not "optional value"?

There are two reasons for this design:

* Gas consumption (zero overhead)
* Nullable values can be supported, like `map<int32, address?>` or `map<K, Point?>`. Returning `V?`, makes it impossible to distinguish between "key exists but value is null" and "key does not exist".

<Aside type="caution">
  Low-level content below, not required for using maps
</Aside>

TVM dictionaries store binary data. Having a `map<K, int32>` and doing `m.set(k, 10)`, this "10" is actually 0x0000000A (automatically packed by the compiler).
All TVM instructions for reading return slices, so at some point, those bits should be decoded back to "10".

TVM instructions put two values on a stack: `(slice -1)` or `(null 0)`. If a choice is to return `V?`, the compiler needs to do something like

```ansi theme={null}
IF stack[0] == -1:
    decode stack[1] to V
    transform V to V?
ELSE:
    drop stack[1]
    transform null to V?
```

Then, at usage, it's compared null:

```tolk theme={null}
val v = m.get(k);   // internally, IF ELSE: for decoding
if (v != null) {    // one more IF: for checking
    ...
}
```

So, it results in two runtime checks and three TVM continuations.

That's why instead of `V?`, a special struct is returned:

```tolk theme={null}
fun map<K, V>.get(self, key: K): MapLookupResult<V>;

struct MapLookupResult<TValue> {
    private readonly rawSlice: slice?
    isFound: bool
}

fun MapLookupResult<TValue>.loadValue(self): TValue {
    return TValue.fromSlice(self.rawSlice!)
}
```

This struct directly maps onto the TVM stack: `(slice -1)` or `(null 0)`.
The condition `if (r.isFound)` naturally checks the top element (automatically popped).
Followed by auto-deserialization at `r.loadValue()` when `rawSlice` is left on the top.

Moreover, some other functions return the same struct. For example, `m.setAndGetPrevious`:

```tolk theme={null}
val prev = m.setAndGetPrevious(1, 100500);
// NOT `if (prev != null)`, but
if (prev.isFound) {
   prev.loadValue()   // 10
}
```

Overall, this provides zero overhead compared to plain dictionaries.

## Stack layout and serialization

An empty map is backed by TVM `NULL`, serialized as '0'. A non-empty map is TVM `CELL`, serialized as '1'+ref.

For details, follow [TVM representation](/languages/tolk/types/overall-tvm-stack) and [Serialization](/languages/tolk/types/overall-serialization).
