How to create a map in a Solidity smart contract

A map in Solidity is like a dictionary in Python or a map in Javascript. It allows you to save data that you want to retrieve in a function using a key to value pair relationship. Mappings are incredibly useful to create associations between different data types (for example: an address and an amount or a uint and a bool). A couple of items to note about maps:

  • They are great for fast lookups.
  • Map do not have a length.
  • By default you cannot iterate through a mapping. 
  • In order to iterate through a mapping you need to use a combination of a map and store the keys in an array.
  • You cannot determine or get the size of a map.
  • Mappings can only be used for state variables that act as storage reference types.

Maps are created with the syntax “mapping(keyType => valueType)" and they are defined like any other variable type in Solidity. See the example below.

Mapping (key => value) public myMapNAME;

Valid key types can be string or byte which translates to uint, bool or address. Valid value types can be any type. This makes mappings very flexible and convenient to use.

Example map

Lets create a sample contract that uses a map. The contract below is a simple contract that does not have anything defined in it. We will expand on this contract to demonstrate how a map is constructed and functions.

pragma solidity ^0.8.0;

//Sample contract is an example
contract SampleContract {
    

    //nothing is defined in this contract


}

Try it in Remix

Lets extend the code above and add a map to our contract. This map will save a number “value” to a corresponding address “key” and save the data to the block chain. In the code below we will define the map and create a function that sets data to the map.

pragma solidity ^0.8.0;

//Sample contract
contract SampleContract {
    
    //state variable
    //added a map that takes an address "key" and a number "value"
    //the name of the map is called myMap    
    mapping (address => uint) public myMap;
 
    
    //this function sets a value to a specific address
    //the data is saved to myMap on the block chain 
    function set(address _addr, uint _i) public {
        myMap[_addr] = _i;
    }
}

Try it in Remix

Read an item in the map

To read an item in the map you use a get function.  It is similar to how we access data in an array.

pragma solidity ^0.8.0;

//Sample contract 
contract SampleContract {
    
    //state variable
    //added a map that takes an address "key" and a number "value"
    //the name of the map is called myMap  
    mapping (address => uint) public myMap;


    
    //this function sets a value to a specific address
    //the data is saved to myMap on the block chain 
    function set(address _addr, uint _i) public {
        // Update the value at this address
        myMap[_addr] = _i;
    }

    
    //this function gets a value from a specific address in the map
    //If a value was not set the function will return the default value of 0.
    function get(address _addr) public view returns (uint) {
        return myMap[_addr];
    }

}

Try it in Remix

Remove an item in the map

Lets add a remove function to the contract so we can delete items in the map. Now the contract below contains:

  • a map defined as myMap which is a state variable saved to the block chain.
  • a function to set a value to a specific address.
  • a function to get the value saved for an address.
  • and finally a function to remove the value you set for an address.

pragma solidity ^0.8.0;

//Sample contract
contract SampleContract {
    
    //state variable
    //added a map that takes an address "key" and a number "value"
    //the name of the map is called myMap  
    mapping (address => uint) public myMap;

    
    
    //this function sets a value to a specific address
    //the data is saved to myMap on the block chain 
    function set(address _addr, uint _i) public {
        myMap[_addr] = _i;
    }
    
    
    
    //this function gets a value from a specific address from the map
    //If a value was not set the function will return the default value of 0.
    function get(address _addr) public view returns (uint) {
        return myMap[_addr];
    }
    
    
    
    //this function deletes a value from the map and sets it to the default value of 0
    function remove(address _addr) public {
    delete myMap[_addr];
    
    }

}

Try it in Remix

In the contract below I changed the value type from uint to bool. This is an example of how you can keep track of an address that answers a true or false statement. As an example: Does this address have permission? Is this address an owner? and so on.

pragma solidity ^0.8.0;

//Sample contract
contract SampleContract {
    
    //state variables
    //added a map that takes an address "key" and a number "value"
    //the name of the map is called myMap  
    mapping (address => bool) public myMap;

    
    
    //this function sets a value to a specific address
    //the data is saved to myMap on the block chain 
    function set(address _addr, bool _i) public {
        myMap[_addr] = _i;
    }
    
    
    
    //this function gets a value from a specific address from the map
    //If a value was not set the function will return the default value of 0.
    function get(address _addr) public view returns (bool) {
        return myMap[_addr];
    }
    
    
    
    //this function deletes a value from the map and sets it to the default value of 0
    function remove(address _addr) public {
    delete myMap[_addr];
    
    }

}

Try it in Remix

Nested maps

Now lets take this mapping exercise one step further and create a nested map. A nested map is where a key is associated to a value type of another map. See the code below.

mapping (address => mapping (address => uint256)) public myMap;

There are many uses cases that need a nested map so to illustrate the concept lets use the following example.

Bob has an account that contains 20 ETH. He wants to give Alice an allowance of 5 ETH and Cindy an allowance of 3 ETH. You can use a nested map to track this scenario. In relational database architecture, this relationship is called a one-to-many: an owner can grant multiple spenders the ability to spend tokens on the owners behalf.

This example of a nested mapping can be found in the ERC20 standard contract in the allowance function. This is a very common use case.

Below is an example of a nested map in an allowance contract.

pragma solidity ^0.7.6; 

contract allowance {
    //mapping an address to another mapping.  This is a one to many relationship
    mapping(address => mapping(address => uint)) public allowance;
    
    

    //this function returns the allowance/amount that the owner permissioned to the spender 
    function get(address _addrOwner, address _addrSpender) public view returns (uint) {
        return  allowance[_addrOwner][_addrSpender];
    }
    


    //this function sets the amount of allowance the owner gives to the spender
    function set(address _addrOwner, address _addrSpender, uint _amt) public {
        allowance[_addrOwner][_addrSpender] = _amt;
    }
    


    //this function removes the spenders allowance
    function remove(address _addrOwner, address _addrSpender) public {
        delete allowance[_addrOwner][_addrSpender];
    }
}

Try it in Remix

The best way to learn how a map works is to experiment and create a smart contract in Remix.

This code is for learning and entertainment purposes only. The code has not been audited and use at your own risk. Remember smart contracts are experimental and could contain bugs.

Click here for more information about how to use the Ethereum test network and how to obtain test ETH.

Next Review – Enum in a Solidity smart contract

Ledger Nano X - The secure hardware wallet

Leave a Reply