19

I have a mapping in my contract like below:

mapping (address => uint) public voterCount;

To calculate the total votes, I would like to loop through the mapping and sum the uint values. How do I do this in solidity? One solution is to use another variable say totalVotes that is incremented every time voterCount is updated. I am trying to see if there is a cleaner solution where I don't need an additional variable to keep track of the count.

11

You can't loop through the keys or count the keys in a mapping (but patterns exist for accomplishing such functionality using a few more variables).

You're on the right path, tracking it yourself. For example:

pragma solidity ^0.4.8;

contract Vote {

  uint public totalVotes;

  mapping (address => uint) public voterCount;

  function vote(uint votes) returns(bool success) {
    totalVotes += votes;
    voterCount[msg.sender] += votes; // cumulative
    return true;
  }
}

You wouldn't want to iterate over the voters in the contract in any case because that would put an upper bound on the number of voters before the system stops working - due to rising transaction cost hitting the block gas limit.

I can show you how to make the voter list iterable but it's not obvious that it's really needed to count the votes as we go. Keeping it simple for first answer.

Hope it helps.

  • I really need to iterate over a mapping, Because I having a struct of person, and they have an amount of tickets, then I need to loop over the array to pick one random and give him a price depending on how many tickets they have. – Mago Nicolas Palacios Sep 22 '17 at 0:27
  • 1
    It's a dead end to require something that cannot be done. It might help to consider two separate issues. Issue one: The data organization. Use mappings for random access when you know the keys, and arrays for lists of things you need to enumerate (avoid loops in Solidity!). They can be used together - see Mapped Struct with Index here: ethereum.stackexchange.com/questions/13167/…. Issue 2: The random selection. It sounds like you need a source of randomness. You can hop directly after you solve organization and choice. – Rob Hitchens - B9lab Sep 22 '17 at 1:28
2

As it's true that you cannot iterate mappings directly, you could do it by knowing all (or some) of the keys. You could do it by:

  • Saving the keys in an array (good for address values, loop trough all addresses in the array)
  • Keeping the keys predictable (integer values, loop from firstKey to lastKey)

Why would you do that?

At the cost of more storage you can continue to reap the benefits of fast retrieval by key.

But you must keep in mind some of the limitations of iterations, like having too many steps to the point that the iteration exceeds the maximum amount of gas.

1

You can loop structs into a mappings using FOR or DO/WHILE loops like that:

pragma solidity ^0.5.7;

/*
 * @dev Testing the loop mapping
 */
contract MappingLoop {
/*
 * @dev Input sample data in structs and mappings
 */
constructor() public {
    structs[0][0].addr = msg.sender;
    structs[0][0].amount = 10;
    structs[0][1].addr = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
    structs[0][1].amount = 20;
    totalStructs[0] = 1; // Struct couter
}

/*
 * @dev Struct structure and mapping
 */
struct Struct {
    address addr;
    uint256 amount;
}
mapping (uint256 => mapping (uint256 => Struct)) internal structs;
mapping (uint256 => uint256) public totalStructs;

/*
 * @dev Function looping FOR
 */
function simpleLoopFor (uint256 id) public returns(uint256) {
    require (totalStructs[id] > 0);
    uint256 totalAmount;
    for(uint8 i=0; i<= totalStructs[id]; i++){
        address addr   = structs[id][i].addr;
        uint256 amount = structs[id][i].amount;
        log0(bytes32(uint256(addr)));
        totalAmount = totalAmount + amount;
    }
    return totalAmount;
}
}

Here we are counting total value of all amounts in the Structs, including in the mapping... Our Inputs are: 10 and 20 uint256s... And Solidity counting it well as 30.

So, all we need it is a totalStructs counter to use for() looping in Solidity.

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.