Would it be possible to increase all the values of a mapping or array (without a loop)?

Example:

mapping(address => uint) score;

uint[] values;

function increase_scores() public {
    // Example pseudo-code for mapping and array:
    score.all += 1; // ??
    values += 1; // Ex.Something like the python map(lambda x: x+1,list) ??
}

Edit: To provide more context, I have a mapping with users and scores. Every time I call the function increase_scores, I would like to increase the score of all the users of the mapping. Therefore, I wouldnt want to loop on the full array as I may run out of gas, if there are many users.

Edit: The map example of python is also not a good idea as at the end as it has to iterate over all the array or mapping..it will run out of gas too. There is no difference with the loop.

  • 2
    Mappings are not iterable, but it is possible to implement a data structure on top of them. This is an example Iterable Mapping – I.B Dec 12 '17 at 4:00
up vote 4 down vote accepted

As Rob suggests, what you could do -since iterating a mapping is not possible, and if it was an array, it would be expensive- is to compute the values on the fly.

Say you have several scores mapped to addresses.

Address - Score

0x1 - 3

0x2 - 4

0x3 - 2

Your original intention was to iterate over every element of the mapping to increase the scores, which is not doable.

What you could do is to add a state variable uint scoreIncrement which keeps track of how many times the score has been increased for all scores.

function increase_scores() public {
    scoreIncrement++;
}

Then, you have a function which returns the score of an element, plus the scoreIncrement.

function getScore(address _a) public returns (uint){
  return scores[_a] + scoreIncrement;
}

So, instead of directly accessing an element of scores mapping, you would call this function.

You could add even more logic to this. You might not want to increase every single score that gets added in the future as well. So you might want to play with timestamps too or group addresses, so a given scoreIncrement only affects certain addresses and not all of them.

  • 1
    Note that this is only viable if the increase is meant to include users not already in the mapping. (I'm guessing that perhaps the score should be increased only for the current set of players, and new players that join later should not benefit from the score increase.) – smarx Dec 12 '17 at 15:07
  • Exactly. That's why I mentioned there might be a few more steps involved in this, depending what wants to be achieved. They would have to keep track of when each score increase is done, and only apply it to users who sign up timestamp is previous to the date the increase was made on. – pabloruiz55 Dec 12 '17 at 15:09
  • Any thoughts on what if the number of groups that a player is member is not limit ? I mean I would have to iterate on the full array with all the groups that the player is member.. and assuming he can be part of thousands of groups...I can also run out of gas. Sorry to extend the question but do you have any recommendation in that use case? – chuseuiti Dec 13 '17 at 5:40

No.

It would require iteration over a list of the keys and writes to each mapped score. It would be prohibitively expensive at scale.

No one can suggest precisely how to address your use-case without more information (edit your question?) but I might suggest that some of these cases can be achieved with functions that do a little time-based or event-based math to compute the result on the fly.

Is it possible to figure out what the storage would say if the brute force update was used?

Instead of returning the exact contents of a storage location, return a function of the storage and the other variables (how much time has passed, for example).

UPDATE

Instead of

mapping(address => SomeStruct) public someStructs;

and a "brute force" update

function massiveUpdate() public onlyOwner ( for(i=0; i<number ...

Consider running a computation on the stored state to determine the current state. In this little contrived example, everyone in the game is supposed to gain a point every time a new block is discovered. We'll do it without writing to storage.

mapping(address => SomeStruct) someStructs; // not public

function getAScore(address key) public constant returns(uint) {
  uint score = someStructs[key].score; // a property that needs periodic batch updates
  uint lastUpated = someStructs[key].lastUpdated; // timeStamp of last update (as blockNumber)
  uint elapsed = block.number - lastUpdated;
  uint actualScore = score + elapsed;
  return actualScore; // everyone receives +1 every 15 seconds.
}

Hope it helps.

  • Hi! sorry but what do you mean by brute-force update? and by function of the storage? – chuseuiti Dec 12 '17 at 4:33

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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