Solidity – How to Push a Struct into a Mapping of Structs

solidity

I'm prototyping a simple smart contract for donations. My intention is to have a mapping for balances and another mapping for structs. The issue is that I get the following error:

TypeError: Member "push" not found or not visible after argument-dependent lookup in struct donMapping.Donation storage ref. --> sc.sol:79:9: | 79 | donations[_recipient].push(Donation(id++,amount,_donor,_msg,block.timestamp)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^

I'm guessing that donations[_recipient] is not recognized as a struct, so push can't be used there (similar syntax as I use for pushing into a regular struct i.e. donation.push(Donation(…))).

What is the proper way to push a struct into a mapping of structs? I tried searching through existing questions in stackexchange with this error, but none seemed to apply to this case. Here's the full prototype code:

contract donMapping{
struct Donation {
    uint id;
    uint amount;
    string donor;
    string msg;
    uint timestamp; //seconds since unix start
}
uint amount = 0;
uint id = 0;
mapping(address => uint) public balances;
mapping(address => Donation) public donations;
Donation[] public donation;

function donate(address _recipient, string calldata _donor, string calldata _msg) public payable {
    require(msg.value > 0, "The donation needs to be >0 in order for it to go through");
    amount = msg.value;
    balances[_recipient] += amount;        
    donations[_recipient].push(Donation(id++,amount,_donor,_msg,block.timestamp));
  }
}

Thanks!

Edit: Thanks to bguiz's answer I realized what I wanted to do was have a mapping of struct arrays (so the only thing missing was the [] on the second mapping definition). Here's the working solution I came up with:

contract mappingOfArrayOfStructs{
    struct Donation {
        uint id;
        uint amount;
        string donor;
        string msg;
        uint timestamp; //seconds since unix start
    }
    uint amount = 0;
    uint id = 0;
    mapping(address => uint) public balances;
    mapping(address => Donation[]) public donationsMap;

    function donate(address _recipient, string calldata _donor, string calldata _msg) public payable {
        require(msg.value > 0, "The donation needs to be >0 in order for it to go through");
        amount = msg.value;
        balances[_recipient] += amount;        
       donationsMap[_recipient].push(Donation(id++,amount,_donor,_msg,block.timestamp));
  }
  
   function donations_getter(address _recipient, uint _id) public view returns (Donation[] memory){
        return donationsMap[_recipient];//shows up as a tuple
  }  
}

Best Answer

Try replacing this line:

donations[_recipient].push(Donation(id++,amount,_donor,_msg,block.timestamp));

with this with this line:

donations[_recipient] = Donation(id++,amount,_donor,_msg,block.timestamp);

The square bracket syntax already identifies the key that you wish to set the new value of in the mapping.


The above answers your question about a mapping of structs, however, I notice that you also have an array of the same struct.

Donation[] public donation;

Note that the above suggestion will not add the struct to this array as well.

If that is the intent (where you wish to maintain both a mapping and an array), I suggest you name these variables dontationsMap and donationsList instead... and then replace the same line above with:

donationsMap[_recipient] = Donation(id++,amount,_donor,_msg,block.timestamp);
donationsList.push(Donation(id++,amount,_donor,_msg,block.timestamp));

(this is where you'd actually want to use .push())

Related Topic