How to iterate through Solidity mappings?


Solidity has many data types for storage, but you must be wise to pick the right one for the job.

The most efficient one for direct data lookup is, of course, a hash table, which is called mappings in Solidity.

The downside of this kind of key-value store is that, you’d have all the keys necessary to iterate through all the values. So the simple workaround is to literally store the keys in an iterable array alongside the mappings.

In the following example, let’s store the value of coins that each address is holding. We’ll set the address to be the key and the value will be the coin value. Then we’d store the address in the array for us to be able to iterate over the mapping:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

contract IterableMappingContract {
  address[] bagHolders;
  mapping(address => uint) values;
}

Above is a very rudimentary storage example, so you’d have to insert and delete the values manually into the mapping and array. To make things easier solidity-by-example provides an example library, which you could use to iterate through mappings:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

library IterableMapping {
    // Iterable mapping from address to uint;
    struct Map {
        address[] keys;
        mapping(address => uint) values;
        mapping(address => uint) indexOf;
        mapping(address => bool) inserted;
    }

    function get(Map storage map, address key) public view returns (uint) {
        return map.values[key];
    }

    function getKeyAtIndex(Map storage map, uint index) public view returns (address) {
        return map.keys[index];
    }

    function size(Map storage map) public view returns (uint) {
        return map.keys.length;
    }

    function set(Map storage map, address key, uint val) public {
        if (map.inserted[key]) {
            map.values[key] = val;
        } else {
            map.inserted[key] = true;
            map.values[key] = val;
            map.indexOf[key] = map.keys.length;
            map.keys.push(key);
        }
    }

    function remove(Map storage map, address key) public {
        if (!map.inserted[key]) {
            return;
        }

        delete map.inserted[key];
        delete map.values[key];

        uint index = map.indexOf[key];
        uint lastIndex = map.keys.length - 1;
        address lastKey = map.keys[lastIndex];

        map.indexOf[lastKey] = index;
        delete map.indexOf[key];

        map.keys[index] = lastKey;
        map.keys.pop();
    }
}

contract TestIterableMap {
    using IterableMapping for IterableMapping.Map;

    IterableMapping.Map private map;

    function testIterableMap() public {
        map.set(address(0), 0);
        map.set(address(1), 100);
        map.set(address(2), 200); // insert
        map.set(address(2), 200); // update
        map.set(address(3), 300);

        for (uint i = 0; i < map.size(); i++) {
            address key = map.getKeyAtIndex(i);

            assert(map.get(key) == i * 100);
        }

        map.remove(address(1));

        // keys = [address(0), address(3), address(2)]
        assert(map.size() == 3);
        assert(map.getKeyAtIndex(0) == address(0));
        assert(map.getKeyAtIndex(1) == address(3));
        assert(map.getKeyAtIndex(2) == address(2));
    }
}

Also, don’t foreget to check out Iterable Mappings in the docs.

Happy coding!