1
pragma solidity ^0.4.17;
contract Campaign{
    struct Transaction {
        address Creditor;
        uint Amount;
    }
    Transaction[] public Transactionss;
    mapping(address => Transaction) public TransactionsMap;

    function addTransaction(address _address,uint _amount) public {

        Transaction memory newRequest = Transaction({
           Creditor: _address,
           Amount: _amount });
        Transactionss.push(newRequest); 
        TransactionsMap[_address]=Transactionss[Transactionss.length-1];


    }
    function Testing()public {
        addTransaction(0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8,100);
        TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8].Amount++;
    }


    function a()public view  returns (uint){
        return TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8].Amount;
    }

    function b()public view  returns (uint){
        return Transactionss[0].Amount;
    }


}

Running function a will give 101.

Running function b will give 100.

Why? How to fix it?

Rob Hitchens
  • 55,151
  • 11
  • 89
  • 145
Luk Aron
  • 111
  • 3
  • No, it is not possible. In solidity for array and mapping you cannot assign references it will always copy the structs. – Ismael May 05 '19 at 17:49

2 Answers2

1

I'm looking at other responses and wondering if I misread the question. I'm going with the idea that this is about a method of doing it rather than an expectation that it is done automatically.

It will not be done for you, but this is doable and it is one of the popular storage patterns - Mapped Struct with Index (and delete possibility), a.k.a. Solidity CRUD.

I fiddled with your contract quite a bit to make it into something that sort of works.

Your question actually hints at the way to solve it. Indeed, the struct itself will need to "point" to the row in an index. Something like:

struct Transaction {
    address Creditor;
    uint Amount;
    uint arrayPointer; // <== row in array

There is more than one way to organize things. A popular way is to keep an array of primary keys:

address[] public transactionAddresses;  // <== array of primary keys

So, not the objects, themselves. Just the keys we would need to iterate the list so we can enumerate the keys, so we can fetch keys of interest from ...

mapping(address => Transaction) public TransactionsMap; // map of objects for random access

Great. It's a bi-directional binding. If you have an array row, you have the key so you can inspect the object. If you have the object, you can see what row it is in within the list. We just have to maintain it as we go.

function addTransaction(address _address,uint _amount) public {
    Transaction memory newRequest = Transaction({
        Creditor: _address,
        Amount: _amount,
        arrayPointer: transactionAddresses.push(_address)-1});
    TransactionsMap[_address]=newRequest;
}

I ended up with this:

pragma solidity ^0.4.17;

contract Campaign {

    struct Transaction {
        address Creditor;
        uint Amount;
        uint arrayPointer; // <== row in array
    }
    address[] public transactionAddresses;  // <== array of primary keys
    mapping(address => Transaction) public TransactionsMap; // map of objects for random access

    function addTransaction(address _address,uint _amount) public {
        Transaction memory newRequest = Transaction({
           Creditor: _address,
           Amount: _amount,
           arrayPointer: transactionAddresses.push(_address)});
        TransactionsMap[_address]=newRequest;
    }
    function Testing()public {
        addTransaction(0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8,100);
        TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8].Amount++;
    }
    function a()public view  returns (uint){
        return TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8].Amount;
    }
    function b()public view  returns (address){
        return transactionAddresses[0];
    }
}

You can have a Mapped Struct with Index without the pointer if an append-only process is acceptable. In that case, the pointer in the struct isn't necessary. The main advantage of the pointer is that it facilitates the potential to logically remove an item from the Set as might be necessary if Set members are transient.

The pattern is explained in more detail over here (2017): https://medium.com/robhitchens/solidity-crud-part-1-824ffa69509a

A reusable code base for it (2019) over here: https://github.com/rob-Hitchens/UnorderedKeySet and explainer: https://medium.com/robhitchens/solidity-crud-epilogue-e563e794fde

A comparison of Solidity CRUD to other patterns: Are there well-solved and simple storage patterns for Solidity?

Hope it helps.

Rob Hitchens
  • 55,151
  • 11
  • 89
  • 145
0

I assume you mean that you are calling the Testing function first. Or that the contract name has changed and the Testing function should actually be the constructor. Anyway, I assume it gets called in some way, otherwise both a and b would give you errors as there is nothing stored.

So after the first line of Testing has been executed the array Transactionss has an entry which has value 100 and TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8] also has an entry which has value 100. The second line of Testing increases the value at TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8] by one (++).

So TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8].Amount is 101 and Transactionss[0].Amount has a value of 100 which you also get from your view functions.

Lauri Peltonen
  • 29,391
  • 3
  • 20
  • 57
  • great analysis, however, I really want TransactionsMap[0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8] to refer exactly the object Transactions with address 0xaef0B7Edd5D2E9315027ADFA4642E16a5c85Afd8, so that calling function b will also give 101, how can i do that? – Luk Aron May 05 '19 at 16:42
  • Please see Ismael's comment - there's no direct solution. – Lauri Peltonen May 05 '19 at 18:03