23

I know that it's possible to return a struct through argument destructing:

contract Test {
    struct Point {
        int x;
        int y;
    }

    Point point;

    function getPoint() returns (int x, int y) {
        x = point.x;
        y = point.y;
    }
}

The problem is that I need to return an array of structs:

contract Test {
    struct Point {
        int x;
        int y;
    }

    Point[] allPoints;

    function getPoints() returns (???) {
        ???
    }
}

Is it possible?

7 Answers 7

12

This can be done using the v2 ABI encoder. Let's talk about how to use this encoder, and then how to return an array of structs.

How to use abicoderv2

source: Solidity docs

abicoderv2 is the ABI encoder by default starting in Soldity 0.8. It was originally considered experimental, but as of Solidity 0.6 was given non-experimental status. As such, you're using it by default in 0.8 and up.

To use it from 0.6 to 0.8 (non-inlusive), put the following at the top of the contract (I've generally seen it put directly beneath the pragma declaring the version of Solidity):

pragma abicoderv2;

For Solidity <0.6, use:

pragma experimental ABIEncoderV2

How to return an array of structs

Example code:

struct myStruct {
  address foo;
  uint256 bar;
}
myStruct[] private myStructs;

function getMyStruct() public view returns(myStruct[] memory) {
  return myStruct;
}

The memory was necessary if there was an interface to the contract implementing a skeleton of this function, it may be able to be taken out if there isn't an interface behind it, though I'm not sure you'd want to even if you could. If I get more clarity on this last point, I'll try to update the answer.

9
  • 5
    Nice that it took only 5 years to get there :´D Commented Apr 22, 2021 at 21:54
  • Thanks for noticing the answer! I was kinda worried it would sit at the bottom of the pile for a while. Commented Apr 23, 2021 at 5:39
  • 2
    To be honest, I was aware of this already, but I had completely forgot I ever asked this question. The upvote and the accept are for your necromancer skills :v Commented Apr 26, 2021 at 18:19
  • 1
    for 0.8.7, you have to put abicoder v2 instead of abicoderv2
    – Kaneda
    Commented Oct 14, 2021 at 23:29
  • 1
    @TheRenaissance weird, I was trying to return struct in 0.8.7 but it didn't work without abicoder v2. Maybe I'm mistaken, anyways if docs say it's built-in then that's the case.
    – Kaneda
    Commented Oct 18, 2021 at 18:14
13

From what I've tested and read, you cannot return dynamic arrays from Solidity functions yet, but the developers are planning to include this functionality sometime. References cryptic type error for multiple return values #164 and Return an array from a Solidity function.

In the meantime, you could create a function getNumberOfPoints() to return the number of points, and modify getPoint() to take an index and return the (x, y) of the point in the array pointed to by the index.

2
  • LOL. This means I'm screwed. Thank you very much. I'll leave the question open so you might be able to update your answer when/if this feature is released. Commented May 4, 2016 at 15:28
  • @Henrique Check my answer below, hope that helps.
    – Susmit
    Commented Jun 30, 2017 at 13:52
2

Given certain conditions on how you've implemented your data structure it is possible to do so. For example look at the code below for contract and javascript:

contract Y{
   string head;
   struct Temp{
      address addr;
      string next;
      string current;
   }
mapping (string => Temp) _temp;
function Y(){
    _temp['root'].addr = 0;
    _temp['root'].next = 'root';
    _temp['root'].current = 'root';
    head = 'root';
}
function addNodes(string _current, address _addr){
    string memory _curr = _current;
    _temp[_current].current = _curr;
    _temp[_current].next = head;
    _temp[_current].addr = _addr;
    head = _curr;
}

function getHead() constant returns(string){
    return head;
}

function getNodes(string _current) constant returns (string,string,address){
    string temp1 = _temp[_current].next;
    address _addr = _temp[_current].addr;
    string temp2 = _temp[_current].current;
    return (temp1,temp2,_addr);
}

}

getList.js

 var Head = y.getHead();
 var tempCurrent="";
 var tempAddress="";
 var next="";
 var arr="";
 var length="";
 var temp="";
 var t_Current="";
 var t_Address="";
 for(;next!='root';){
   temp = y.getNodes(Head);
   length = temp.lenght;
   temp = temp +  "";
   arr = temp.split(",");
   next = arr[0];
   tempCurrent = arr[1];
   tempAddress = arr[2];
   Head = next;
   t_Current = t_Current + tempCurrent + ";";
   t_Address = t_Address + tempAddress + ";";
}
var arr_Current = t_Current.split(";");
console.log(arr_Current);
//console.log(t_Current);

Once you have deployed contract and have added data to contract you can simply call getList.js using loadScript("getList.js") from geth and bingo you will have data from all nodes in list .

PS: This is just a demo you can do much more with your contract.

2

Here is the way I came up with to return dynamic local arrays from a method. Am sure using this, one can come up with various ways to get data using arrays.

Solidity Contract Method

function getArrayData() constant returns (bytes32[] _data1, bytes32[] _data2) {

    bytes32[] memory arrData1 = new bytes32[](5);
    bytes32[] memory arrData2 = new bytes32[](5);

    arrData1[0]='Data 1 - 1';
    arrData1[1]='Data 1 - 2';
    arrData1[2]='Data 1 - 3';
    arrData1[3]='Data 1 - 4';
    arrData1[4]='Data 1 - 5';

    arrData2[0]='Data 2 - 1';
    arrData2[1]='Data 2 - 2';
    arrData2[2]='Data 2 - 3';
    arrData2[3]='Data 2 - 4';
    arrData2[4]='Data 2 - 5';

    return (arrData1, arrData2);
 }

NodeJS Call

contractInstance.getArrayData.call((err, res) =>{
      console.log('Data 1: ' + web3.toAscii(res[0][0])); 
      console.log('Data 1: ' + web3.toAscii(res[0][1])); 
      console.log('Data 1: ' + web3.toAscii(res[0][2])); 
      console.log('Data 1: ' + web3.toAscii(res[0][3])); 
      console.log('Data 1: ' + web3.toAscii(res[0][4])); 

      console.log('Data 2: ' + web3.toAscii(res[1][0])); 
      console.log('Data 2: ' + web3.toAscii(res[1][1])); 
      console.log('Data 2: ' + web3.toAscii(res[1][2])); 
      console.log('Data 2: ' + web3.toAscii(res[1][3])); 
      console.log('Data 2: ' + web3.toAscii(res[1][4])); 
    });

Hope this helps!

2

Simply keep track of the indicies, for example by address

Struct[] public objects;
mapping(address => uint256[]) private indicies;
function getIndicies() constant returns (uint256[]) {
  return indicies[msg.sender];
}

Then you can use the following (written as test):

it('should return objects', async () => {
  const indicies = await contract.getIndicies.call({ from: address });
  for (let i in indicies) {
    const index = indicies[i].toNumber();
    //call public abi function of objects
    const object = await contract.objects.call(index);
    console.log(object);
  }
  assert(true, 'balance should be zero');
});
1
  • It will require a trade off, since it will require an state variable which willl cost more gas, also your function to return should be view type to not behave like a transaction Commented Feb 11, 2018 at 21:02
1

With Solidity 0.4.18, I was able to return a dynamic array of structs from a function but wasn't able to deal with it when I called this function from another contract.

4
  • can you please share an example? Commented Jan 20, 2018 at 12:20
  • struct Qryp{ address sender; string senderName; uint created; string message; } Qryp[] qryps; function Qrypper() public { qryps.push(Qryp(0, "ulu", now, "hi")); } function getQryps() public constant returns (Qryp[]){ return qryps; }
    – ulu
    Commented Jan 23, 2018 at 10:06
  • 1
    Showing TypeError: This type is only supported in the new experimental ABI encoder. Use "pragma experimental ABIEncoderV2;" to enable the feature. Using pragma version greater than 0.4.18 Commented Jun 18, 2018 at 2:08
  • Yes, I used this pragma header.
    – ulu
    Commented Jun 19, 2018 at 19:42
1

Return an array of struct from a function

pragma solidity ^0.5.0;
pragma experimental ABIEncoderV2;
contract Money {
  struct People{
    uint id;
    string name;
    uint amount;
  }
  mapping (uint => People) public peoples;
  event votedEvent(uint indexed _candidateId);
  uint public candidateConut;

  constructor() public {
    candidateConut = 0;
    addCandidate("Holder 1");
    addCandidate("Holder 2");
  }
  function addCandidate(string memory _name) public {
    peoples[candidateConut] = People(candidateConut,_name,0);
    candidateConut++;
  }
  //return Single structure
  function get(uint _candidateId) public view returns(People memory) {
    return peoples[_candidateId];
  }
  //return Array of structure Value
  function getPeople() public view returns (uint[] memory, string[] memory,uint[] memory){
      uint[]    memory id = new uint[](candidateConut);
      string[]  memory name = new string[](candidateConut);
      uint[]    memory amount = new uint[](candidateConut);
      for (uint i = 0; i < candidateConut; i++) {
          People storage people = peoples[i];
          id[i] = people.id;
          name[i] = people.name;
          amount[i] = people.amount;
      }

      return (id, name,amount);

  }
  //return Array of structure
  function getPeoples() public view returns (People[] memory){
      People[]    memory id = new People[](candidateConut);
      for (uint i = 0; i < candidateConut; i++) {
          People storage people = peoples[i];
          id[i] = people;
      }
      return id;
  }
}

Your Answer

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

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