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.