9

I'd like to call mapping as arguments of function. I've tried the following code, but it returned the error. Could I call mapping as arguments as a function?

mapping ( uint256 => mapping (bytes32 => bytes32)) public users;

function add( uint256 id, bytes32[] list, mapping ( uint256 => mapping (bytes32 => bytes32) table){
    for( var i=0; i< list.length; i++ ){
        table[id][list[i]] = list[i];
    }
}

Error: Type is required to live outside storage. function add( uint256 id, bytes32[] list, mapping ( uint256 => mapping (bytes32 => bytes32)) table){ ^------------------------------------------------------^ Error: Internal type is not allowed for public or external functions. function add( uint256 id, bytes32[] kv_list, mapping ( uint256 => mapping (bytes32 => bytes32)) table){

8

The second error you get tells you that external functions cannot have internal types as arguments (i.e. structs and mappings). You can solve that one by specifying internal or private for your function.

For the first error, im not sure if it is a bug or not, but you cannot pass mappings as arguments. What you could do is wrap them in a struct and pass the struct as a parameter.

One final notice is that if you declare your mapping as a state variable then you have access to it directly in your function. So in you example table could just be a state variable...especially since you only use it for storage purposes.

0

I'm working on a project where I need to access users like you, while being able to add quickly. For this my current (barebones) data struct looks like

address lastAccess = 0x0;

struct User {
    ...
}


address mappingHead = 0x0;
mapping (address => address) public users;
mapping (address => User) public data;

Adding a user looks like

if(users[msg.sender] == 0x0 && msg.sender != mappingHead) {
  users[mappingHead] = msg.sender;
  mappingHead = msg.sender;

  data[mappingHead] = new User(...);
}

Iteration to find a user looks like

address iterator = lastAccess;
uint256 iteration = 0;

while(iterator != 0x0) {
  if(iteration++ > _maxIterations){
    lastAccess = iterator;
    iterator = 0x0; // Or break
  } else {
    if(data[iterator].x == X){
      ...
    iterator = 0x0; // Or break
    } else {
      iterator = users[iterator];
    }
  }
}

Finding/modifying is O(N) for N _maxIterations in a single call and insertion/reading/accessing is O(1). This implementation does not support deletion, but you could add a "deleted" flag.

Another solution is to keep a bunch of property mappings.

struct User {
    prop_1;
    prop_2;
    ...
    prop_n;
}


mapping (type => address) public prop_1;
mapping (type => address) public prop_2;
...
mapping (type => address) public prop_n;

mapping (address => User) public list;

Accessing is done by

// Switch on the property x

User u = list[prop_x[_prop_x]];

Adding is done by

prop_1[_prop_1] = msg.sender;
prop_2[_prop_2] = msg.sender;
...
prop_n[_prop_n] = msg.sender;

The problem with this implementation is that you will overwrite addresses for repeating properties. You could add an indexing on repeating properties, so as to know how many repeated properties are for each type, as above, but modified

// Redefine prop_1 as
// mapping(type => mapping(uint256 => address)) public prop_1;

if(prop_x[_prop_x][prop_x_count[_prop_x]] != 0x0)
   prop_x_count[_prop_x]++;

prop_x[_prop_x][prop_x_count[_prop_x]] = msg.sender;

This is still subject to attacks on gas limits, but at least you could make partial searches as shown in the original version.


Note: this is untested code.

  • Having a loop without a fixed amount of iterations is not a very good idea in solidity, using anything not O(1) is a very bad idea. If you data grows it can get locked because the block gas limit cannot be enough to process all your data. – Ismael Sep 17 '17 at 4:38
  • I don't think there are that many alternatives to searching and accessing in constant time. It's not possible to make a struct->primitive mapping. Maybe making n parallel mappings of property->address and an address->struct would make access easier, but there's still no way of iterating over all keys. – Cehhΐro Sep 17 '17 at 5:49
  • I've use an inverted map for something similar, but if you can't get rid of the O(N) I'd suggest to rework your data structures. The risk with O(N) constructions is that the contract can get blocked unable to process if N is too large. – Ismael Sep 19 '17 at 16:20
  • I set N as a constant. If the total array is too large for a single call, the contract saves the last accesses index. – Cehhΐro Sep 19 '17 at 16:44
  • It's not O(1) exactly. I'm brig flexible with notation. It's constant per call, but O(n) for the algo. – Cehhΐro Sep 19 '17 at 16:45

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.