0

Here is my code:

contract MappingsC {
    mapping(string => uint) public person;
    
    function addPerson() external {
        person["Jhon"] = 34;
        person["Doe"] = 11;
    }
    
    function getPersonAge(string calldata name) view external returns(uint) {
        return person[name];
    }
    
    function getAllPerson()  view external returns(uint[] memory) {
        uint[] memory entries = new uint[](2);
        for (uint i = 0; i < 2; i++) {
            entries[i] = person[i];
        }
    }
    
}

It gives the following error:

contracts/Mappings.sol:18:33: TypeError: Type uint256 is not implicitly convertible to expected type string memory.
entries[i] = person[i];
^

3 Answers 3

1

What you're doing wrong is that on line 18 you're trying to use a uint256 key, while the correct usage would be to use a key of type string to be access the mapped value.

A mapping doesn't keep track of what keys were assigned. As a workaround, you could define an array to keep track of all the keys:

string[] personeNames;

Then, you would push a new name to it every time you add a new person, as follows:

function addPerson() external {
    person["Jhon"] = 34;
    personeNames.push("Jhon");
    person["Doe"] = 11;
    personeNames.push("Doe");
}

The final step would be to integrate inside the getter function, as follows:

function getAllPerson()  view external returns(uint[] memory entries) {
    entries = new uint[](personeNames.length);
    for (uint i = 0; i < personeNames.length; i++) {
        entries[i] = person[personeNames[i]];
    }
}
1

here's an example of an iterable mapping pattern

pragma solidity ^0.8.14;

import "hardhat/console.sol";

contract Family {

    mapping (address => string) public familyMembers;   // store data
    mapping (address => bool) public isInMap;   // need this as we can't distinguish a zero cause not in the map from is in map but is zero
    address[] public keys;  // something we can enumerate and use each element as the key for the familyMembers map

    constructor () {
        addFamilyMember(address(0x1), "alice");
        addFamilyMember(address(0x2), "bob");
        addFamilyMember(address(0x3), "cam");
        addFamilyMember(address(0x4), "doris");
        addFamilyMember(address(0x5), "");        //  <---
        addFamilyMember(address(0x5), "eve");     //  <---
        addFamilyMember(address(0x6), "fud");     //  <---
        addFamilyMember(address(0x6), "fred");    //  <---

        iterateFamilyMembers();
    }

    function addFamilyMember(address _addr, string memory _name) public {
        
        familyMembers[_addr] = _name;
        if(!isInMap[_addr]) {
            isInMap[_addr] = true;
            keys.push(_addr);
        }
    }

    function iterateFamilyMembers() public view {
        for(uint i; i < keys.length; ++i){
            console.log("address: %s, name: %s", keys[i], familyMembers[keys[i]]);
        }
    }
}

output:

console.log:
 address: 0x0000000000000000000000000000000000000001, name: alice
 address: 0x0000000000000000000000000000000000000002, name: bob
 address: 0x0000000000000000000000000000000000000003, name: cam
 address: 0x0000000000000000000000000000000000000004, name: doris
 address: 0x0000000000000000000000000000000000000005, name: eve
 address: 0x0000000000000000000000000000000000000006, name: fred

this is a pattern for an iterable mapping. we need an array to complement the mapping, as it's iterable. we also put the address of each map element in the array so we can use that as the key to pull the values from the map as we iterate the array.

we also need another map to tell us whether an element has been added already or not. we need that as there is no null in solidity so in some cases there is no way for us to be certain whether a zero or empty string returned from a mapping is because it wasn't found, or it was found and the zero or empty string is a valid value.

In your familyMember / name example, perhaps this wouldn't be an issue, but in other cases it would.

An example of where it would be a problem is a mapping of address and account balance. A balance of zero is valid and common. When we look up the address in the mapping and get a zero, was the address in there with a zero balance or just not there? We need to know that to know whether to add another element to the array or not.

If we didn't want to make the mapping iterable, it probably wouldn't matter why we got a zero or an empty string.

0

Check out Mapped Structs with Delete-enable Index or the simpler pattern above (append only, no delete). Are there well-solved and simple storage patterns for Solidity?

The general idea is mappings for random access and arrays to enumerate members. Neither data structure can do both so you need a higher-order implementation that uses both.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.