3

I have a mapping where the key is the artist name, the value is msg.sender address. In a function, I want to check if the parameter username has already existed in the mapping keys, if yes, i will throw

How do I loop through a mapping of value keys to check and compare with input parameter?

mapping(bytes32 => address) public reserved_names;

function create_artist(bytes32 username, string ipfs) {

   //todo throw exception if artists username already exists

   reserved_names[username] = msg.sender;
5
assert(reserved_names[username] == 0x0);
  • 1
    This must be the chosen answer. It does exactly what the OP asked, but it costs magnificently less, doesn't require another separate index, and is super-terse and therefore very cool! – Thomas Jay Rush Aug 31 '17 at 21:02
3

There currently is no way to iterate through a mapping, a mapping isn't a fixed-length array, it simply decodes its input to a certain memory address via a function and retrieves what is contained within it.

There are two alternatives to what you can do.

One would be to create a variable length byte32 array and add any new usernames you would find to it and iterate through the array to cross-reference the input username.

mapping(bytes32 => address) public reserved_names;
bytes32[] public namesInUse;

function create_artist(bytes32 username, string ipfs) {
    for (uint i = 0; i < namesInUse.length; i++) {
        require(namesInUse[i] != username);
    }
    reserved_names[username] = msg.sender;
    //Rest of your code here
}

This is not the ideal solution because it uses a lot of resources and can lead to an out-of-gas exception if there are many users that sign up to your service.

The efficient way you can solve this is by having a second boolean mapping that is changed to true whenever the username is in use. Because the default value for every boolean mapping is false you won't face any issues. An example implementation can be found below.

mapping(bytes32 => address) public reserved_names;
mapping(bytes32 => bool) public namesInUse;

function create_artist(bytes32 username, string ipfs) {
    require(!namesInUse[username]);
    reserved_names[username] = msg.sender;
    namesInUse[username] = true;
    //Rest of your code here
}

EDIT:

Nikita's answer is much more gas efficient and won't consume any memory at all. What he is taking advantage of is that address variables when initiated point to the 0x0000000000000000000000000000000000000000 address which can be shorthanded to 0x0, not 0x as his answer dictates.

1

The best way is to use "require(reserved_names[username] == 0x0)"

Your code will be like following:

mapping(bytes32 => address) public reserved_names;

function create_artist(bytes32 username, string ipfs) {

   //todo throw exception if artists username already exists
   require(reserved_names[username] == 0x0)

   reserved_names[username] = msg.sender;

   ...
}

Note that using "assert" does not fit in your case. Because of many reasons including that it will use up all user gas! (For more info about assert and require check: Difference between require and assert and the difference between revert and throw)

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.