0

Can you have an array of mappings? I want to have mappings with addresses as keys and structs as the value. I want to store these addresses in an array and iterate through them. so far it looks like this how would I modify this?

pragma solidity ^0.5.0;

contract Ranking{

    struct User {
      string name;
      uint level;
    }

    mapping (address => User) userStructs;        
    address[] public userAddresses;

    function createUser(string name, uint level) {
      userStructs[msg.sender].name = name;
      userStructs[msg.sender].level = level;
      userAddresses.push(msg.sender);            
    }

    function getAllUsers() external view returns (address[]) {
      return userAddresses;           
    }    
}

1 Answer 1

1

I understand this:

I want to have mappings with addresses as keys and structs as the value. I want to store these addresses in an array and iterate through them. so far it looks like this how would I modify this?

but I don't see what this has to do with it:

Can you have an array of mappings?

No. You can put a mapping in a struct, however, and you can make arrays and mappings of those structs, so you should be able to work it out using struct to negotiate between the mapping and the limitations of arrays.

Is your question about merging the mapping and the array into a more concise form? What you're doing is a good start if you need random access to the structs as well as the ability to enumerate the keys. If either of those concerns is not a real requirement, then you might save a little gas and get by with just a mapping or just an array. Otherwise, you need both because they address different requirements.

As a PSA, there is a problem unless you are certain there will never be too many users to fetch them all before running out of gas. Keep in mind that if it runs out of gas, or hits the block gasLimit, the user gets nothing. So, it is a potentially fatal issue.

returns (address[])

If you can't be 100% sure that is not the case, then go for efficiency.

function userCount() public view returns(uint) {
  return userAddresses.length;
}

function getUserAtIndex(uint row) public view returns(address) {
  return userAddresses[row];
}

That approach ensures it will always be possible to fetch the list. It will also be more efficient because it affords the client the possibility of only fetching new information, as needed, instead of always fetching the works, which is a lot of work.

As a general habit, I prefer the latter approach than the former, especially in early formative stages because it works reliably. Toward the end when looking for ways to optimize you might consider (carefully) if return(address[]) is safe in this case (and why) and you might also include both options so there is something to fall back on if the big bang approach falters for some unexpected reason.

It might be worth mentioning that it is perfectly safe to pass arrays around with internal and private functions because only storage pointers (basically, a storage offset) are passed. Not the data. You see it a lot in examples you might be following. For a public function, it implies data equal to the total stored contents of the array, so potentially huge.

Hope it helps.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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