22

I know I can return dynamic byte arrays in Solidity, such as in the following code:

function createMemoryArray(uint size) returns (bytes) {
    // Dynamic memory arrays are created using `new`:
    uint[2][] memory arrayOfPairs = new uint[2][](size);
    // Create a dynamic byte array:
    bytes memory b = new bytes(200);
    for (uint i = 0; i < b.length; i++)
        b[i] = byte(i);
    return b;
}

But is there a way to return something like a dynamic array of strings? I feel like this is just an implementation detail in Solidity, but it would be great if it just serialized everything nicely for me.

10

Is it possible to return an array of strings ( string[] ) from a Solidity function?

Not yet, as this requires two levels of dynamic arrays (string is a dynamic array itself). Doc

However you can return an Array of Bytes32 ( fixed size of 32 byte) So you can try to do something like this ( you can copy paste on Remix to test it )

pragma solidity ^0.4.11;
contract ArrayOfBytes32 {
    address creator;
    bytes32[10] bytesArray; // size must be fixed
  
    function ArrayRR() 
    {
        creator = msg.sender;
        uint8 x = 0;
        while(x < bytesArray.length)
        {
            bytesArray[x] = "myString"; 
            x++;
        }
    }
   
    function getArray() constant returns (bytes32[10])
    {
        return bytesArray;
    }
    
    function getValue(uint8 x) constant returns (bytes32)
    {
        return bytesArray[x];
    }
}

Note that you will have to use web3.toAcsii() doc to convert the result if you use web3 to interact with your contract

| improve this answer | |
  • 1
    Huh, I never would've thought to check the doc FAQs. Nice find and interesting solution! – user9402 Jun 6 '17 at 13:35
  • 1
    Why I can't return a dynamic array of of bytes32 (bytes32[])? – Robert Zaremba Aug 21 '17 at 14:41
  • 2
    You can return a dynamic array of of bytes32 (bytes32[]). What you can't is returning an object which combines two levels of dynamic arrays. – Robert Zaremba Aug 21 '17 at 18:02
  • @RobertZaremba But isn't bytes32 an array itself? Then bytes 32[] is a two dimensional array – Andrey Mar 21 '18 at 14:16
  • 1
    @Andrey Problem is with two levels of dynamic arrays, not a two dimensional arrays. You could have function return two dimensional array of 10 uints each without problem - but you cannot have two dimensional array where both arrays are dynamic. Type "bytes32" just indicate that it allocates 32 bytes in the memory, so it is not dynamic variable - you always know that it will take 32 bytes of memory, therefore it is static variable (cannot change size during runtime). That's why it works :) – Oliver Rydzi Dec 10 '18 at 17:38
2

It is an old issue... But, for newcomers and for completeness sake, there is no problems in Solidity to return dynamic arrays of pairs, or dynamic arrays of strings, if one uses the modern compiler (tested with 0.5.6) and experimental ABI pragma:

    pragma experimental ABIEncoderV2;

    ...

    function createMemoryArray(uint size) public pure returns (uint[2][] memory) {
        uint[2][] memory b = new uint[2][](size);
        for (uint i=0; i < b.length; i++) {
            b[i][0] = i;
            b[i][1] = i * 2;
        }
        return b;
    }

    function createStringArray(uint size) public pure returns (string[] memory) {
        string[] memory b = new string[](size);
        for (uint i=0; i < b.length; i++) {
            b[i] = "test";
        }
        return b;
    }
| improve this answer | |
  • Thanks, this should be the accepted answer – charel-f May 28 at 14:28
0

Return dynamic arrays of structure in 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;
  }
}
| improve this answer | |
  • I don't encourage you to use this in main net, cause it is experimental and a lately article has shown a bug in it. – Majd TL Aug 28 '19 at 14:26
0

Is it possible to return a dynamic array of strings ( string[] ) from a Solidity function?

Yes, you can serialize it to bytes and deserialize it back to string[].

In your smart contract:

function toBytes(string[] strArray)
private
pure
returns(bytes serialized) {
    uint startindex = 0;
    uint endindex = strArray.length - 1;

    require(endindex >= startindex);

    if (endindex > (strArray.length - 1)) {
        endindex = strArray.length - 1;
    }

    //64 byte is needed for safe storage of a single string.
    //((endindex - startindex) + 1) is the number of strings we want to pull out.
    uint offset = 64 * ((endindex - startindex) + 1);

    bytes memory buffer = new bytes(offset);
    string memory out1 = new string(32);


    for (uint i = startindex; i <= endindex; i++) {
        out1 = strArray[i];

        stringToBytes(offset, bytes(out1), buffer);
        offset -= sizeOfString(out1);
    }

    return (buffer);
}

function stringToBytes(uint _offst, bytes memory _input, bytes memory _output)
private
pure {
    uint256 stack_size = _input.length / 32;
    if (_input.length % 32 > 0) stack_size++;

    assembly {
        stack_size: = add(stack_size, 1) //adding because of 32 first bytes memory as the length
        for {
            let index: = 0
        }
        lt(index, stack_size) {
            index: = add(index, 1)
        } {
            mstore(add(_output, _offst), mload(add(_input, mul(index, 32))))
            _offst: = sub(_offst, 32)
        }
    }
}


function sizeOfString(string memory _in)
private
pure
returns(uint _size) {
    _size = bytes(_in).length / 32;
    if (bytes(_in).length % 32 != 0)
        _size++;

    _size++; // first 32 bytes is reserved for the size of the string
    _size *= 32;
}

}

Then to deserialize it back to string[] (using js) use the below function:

function hexBytesToStr(hex) {
    let str = '';
    for (let i = 0; i < hex.length; i += 2) {
        let v = parseInt(hex.substr(i, 2), 16);
        if (v) str += String.fromCharCode(v);
    }

    let params = [];
    let res = "";
    for (let i = 0; i <= str.length; i++) {
        if (str.charCodeAt(i) > 31) {
            res = res + str[i];
        }
        else {
            params.push(res);
            res = "";
        }
    }
    params.pop();

    return params;
}
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy