BlockchainSolidity9 min readUpdated

Mapping in Solidity: The Workhorse Key-Value Lookup

By Mudassir Khan — Agentic AI Consultant & AI Systems Architect, Islamabad, Pakistan

Cover illustration for: Mapping in Solidity: The Workhorse Key-Value Lookup

Section 01 · Definition

What is a mapping in Solidity?

A mapping is a key-value store. You give it a key and it gives you a value. It is the most-used data structure in Solidity by a wide margin.

Quick answer

What is a mapping? mapping(K => V) is Solidity's built-in key-value type. The compiler reserves one storage slot for the mapping itself, then computes an actual storage location on demand by hashing the key together with the slot number. Every possible key is implicitly present and starts at the default value of the value type — zero for numbers, address(0) for addresses, false for bools, '' for strings.

solidity
mapping(address => uint256) public balances;
mapping(address => bool)    public allowed;
mapping(uint256 => address) public ownerOf;            // ERC721 style

// Nested mapping — common for ERC20 allowance(owner, spender)
mapping(address => mapping(address => uint256)) public allowance;

Declaring a mapping public gives you a free getter named after the mapping. Calling balances(someAddress) from a wallet returns the stored value. Inside the contract you read it as balances[someAddress].

Section 02 · How it works

The hash trick that makes lookups O(1)

A mapping does not store a list of entries. It stores nothing. Each access computes a deterministic storage slot from the key.

Diagram showing a mapping lookup combining a key with the slot index through keccak256 to find the storage slot.
The slot for balances[user] is keccak256(user || slotOfBalances). No iteration, no enumeration.

Imagine an unbounded sparse array indexed by every possible address. Most of the array is implicitly zero. Writing balances[alice] = 100 stores 100 at the address derived from keccak256(alice || slotOfBalances). Reading the same key reproduces the same hash and finds the same slot. There is never any list of who has been seen — the mapping has no notion of cardinality. To compute the actual value slot for a given mapping and key without writing a script, use the mapping calculator inside the Solidity Storage Layout Visualizer.

That is why you cannot iterate

There is nothing to iterate. The mapping has no list of keys, no internal counter, no way to ask 'what was inserted'. If you need enumeration — for example to pay every staker at the end of an epoch — you maintain a parallel address[] array and update it whenever you add a new participant.

Section 03 · Common patterns

Three patterns that show up in every contract

If you read any production contract, you will see these three mapping shapes within the first 40 lines.

solidity
// 1. Per-user state
mapping(address => uint256) public balances;

balances[msg.sender] += msg.value;

// 2. Mapping to a struct (the workhorse)
struct Position {
    uint256 collateral;
    uint256 debt;
    uint64  openedAt;
}
mapping(address => Position) public positions;

positions[user].collateral += amount;

// 3. Nested mapping for two-key lookup
mapping(address => mapping(uint256 => bool)) public hasClaimed;

hasClaimed[msg.sender][campaignId] = true;

Pattern 2 is by far the most common. A mapping to a struct gives you a clean way to bundle several fields under one identifier. Token contracts, lending pools, vaults, and prediction markets all use it.

Section 04 · Iterating safely

The enumerable mapping pattern

When you genuinely need to walk every entry, pair the mapping with an array of keys.

solidity
mapping(address => uint256) public balances;
address[] public users;                          // parallel index
mapping(address => bool) private isKnown;

function deposit() external payable {
    if (!isKnown[msg.sender]) {
        users.push(msg.sender);
        isKnown[msg.sender] = true;
    }
    balances[msg.sender] += msg.value;
}

function totalUsers() external view returns (uint256) {
    return users.length;
}

Notice the safety check — push to the array only the first time we see this address, otherwise the array would grow on every deposit. OpenZeppelin's EnumerableSet ships exactly this pattern in audited form.

Beware: iterating a large dynamic array on chain can hit the block gas limit and brick the function. If you expect tens of thousands of entries, do the iteration off chain by reading events instead.

Section 06 · FAQ

Frequently asked questions

Can I iterate a mapping in Solidity?

No. The mapping does not store a list of keys, so there is no way to walk them. If you need iteration, maintain a parallel array of keys and push to it the first time each key is used. EnumerableSet from OpenZeppelin packages this safely.

What does balances[someUnknownAddress] return?

Zero. Every key in a mapping is implicitly present at the default value of the value type. For uint256 that is 0; for bool it is false; for address it is address(0). You cannot tell the difference between 'never written' and 'written and then set back to default'.

Is mapping cheaper than an array?

For lookups by key, yes — a mapping access is one keccak256 plus one SSTORE/SLOAD. An array lookup by index is similar in cost. The big difference is iteration: arrays can be walked for a known cost; mappings cannot be walked at all.

How do I delete an entry from a mapping?

Use the delete keyword: delete balances[user]. This resets that key to the default value of the value type, which refunds some gas. Note this does not remove the key from any parallel array of seen keys — you have to manage that separately.

Can mapping keys be a struct or another mapping?

No. The key must be a value type — uint, int, address, bool, bytesN, or a fixed-size enum. Reference types like string, bytes, struct, or mapping cannot be keys directly. If you need a composite key, hash the parts together with keccak256 and use the resulting bytes32 as the key.

Written by Mudassir Khan

Agentic AI consultant and AI systems architect based in Islamabad, Pakistan. CEO of Cube A Cloud. 38+ agentic AI launches delivered for global founders and CTOs.

View agentic AI consulting serviceSee ChainTrust case study

Related service

Agentic AI Consulting

See scope & pricing →

Related case study

ChainTrust Compliance Engine

Read case study →

More on this topic

Need an AI systems architect?

Book a 30-minute architecture call. I will sketch the high-level design for your use case and give you an honest view of the trade-offs.

Book a strategy call →