2

I am building a DApp game and I am experiencing a problem. Let me describe it. I have a struct:

struct NPC {
    uint id;
    string name;
    uint level;
}

Then I have an array that stores all the NPCs:

NPC[] public NPCs;

And mapping that maps from city to NPC:

mapping (uint => NPC[]) public cityNPCs;

When I create new NPC, I push it to both, array and mapping:

function createNpc(string memory _name, uint _level, uint _cityId)
    public
    returns (bool success)
{
    uint newId = NPCs.length;
    NPC memory newNpc = NPC(newId, _name, _level);
    NPCs.push(newNpc);
    cityNPCs[_cityId].push(NPCs[newId]);
    return true;
}

The problem is that when NPC struct is updated in the array, the updates are not reflected in mapping and vice versa. So, is there are a way to solve this problem by somehow making array and mapping elements in sync?

Btw, Solidity version:

pragma solidity >=0.5.0;
pragma experimental ABIEncoderV2;

1 Answer 1

4

Since each object is obviously cloned (i.e., one object in the array and another object in the mapping), you can add the object to the array, and then add its index in the array to the mapping.

In other words, instead of doing this:

mapping (uint => NPC[]) public cityNPCs;
...
NPCs.push(newNpc);
cityNPCs[_cityId].push(NPCs[newId]);

You can do this:

mapping (uint => uint[]) public cityNPCs;
...
NPCs.push(newNpc);
cityNPCs[_cityId].push(NPCs.length - 1);

Then change the rest of your code respectively.

4
  • The main reason why I wanted to store the whole struct and not just an id is that when my frontend wants to retrieve all the list, I can easily get the whole list from mapping and store it in React state. With your approach I will need to fetch the list of IDs, then iterate over and fetch individual items and push them in JavaScript array and then save to React store.
    – EtherPaul
    Feb 25, 2020 at 19:54
  • @HonorLT: Then add in your contract a function which takes _cityId, builds up an array of NPC objects according to the indexes in cityNPCs[_cityId], and returns that array. Feb 25, 2020 at 19:57
  • If that array becomes very large will I risk exceeding block gas limit?
    – EtherPaul
    Feb 25, 2020 at 20:32
  • 1
    @HonorLT: It can and should be a view function, which means that it doesn't require any gas. Unless, of course, you call it from some other non-view function, in which case that other function may exceed block gas limit. But if you're planning to call it only from an off-chain script, then there shouldn't be any problem here AFAIK (perhaps Etherscan will have a hard time displaying this array, or your off-chain process will run out of memory, but that would happen regardless of whether or not you use this function, because the same array will end up in that process memory). Feb 25, 2020 at 21:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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