26

For some reason solidity doesn't allow to push values into memory array

Member "push" is not available in bytes32[] memory outside of storage.

Here is sample contract code:

pragma solidity ^0.4.21;

contract Foo {
    function getRange(uint n) public pure returns(uint[]) {
        uint[] memory result;
        for (uint i = 0; i < n; i++)
            if (someCondition(i))
               result.push(i);
        return result;
    }
}

I could allocate maximum possible array of size n and then shrink it, but it may hurt performance (n could be in order of magnitude of 100000 while final length of result is 0..100). For example, someCondition could be isPrime. In this case we have large N (which makes impossible to preallocate an array) and small list of resulting prime numbers.

How could it be done?


I ended up with preallicating array of size n and then shrinking it once I know it's final length:

function getRange(uint n) public pure returns(uint[]) {
    uint tookCount = 0;
    uint[] memory result = new uint[](n);
    for (uint i = 0; i < n; i++)
        if (someCondition(i)) {
            result.push(i);
            tookCount++;
        }

    uint[] memory trimmedResult = new uint[](tookCount);
    for (uint j = 0; j < trimmedResult.length; j++) {
        trimmedResult[j] = result[j];
    }
    return trimmedResult;
}
4
  • 1
    push isn't available for memroy array read the documentation solidity.readthedocs.io/en/v0.4.21/types.html. for your new edit you have to omit the pure keyword and either you add elements using an array indice or by using a storage array
    – Badr Bellaj
    Commented Apr 26, 2018 at 11:53
  • @BadrBellaj I cannot use a storage because this function should not affect contract. Commented Apr 26, 2018 at 12:15
  • if you know the array size create a memory array with length n : uint[] memory result = new uint[](n);
    – Badr Bellaj
    Commented Apr 26, 2018 at 12:43
  • 1
    @BadrBellaj no, I don't. I tried to enhance my question to make it clearer, thank you. Commented Apr 26, 2018 at 13:13

4 Answers 4

39

Dynamic arrays are only available in storage, not in memory. In your case, the size of the result array is known upfront (n). So, you can just declare an array with length of n. Then you can fill it up using i, which goes from 0 to n - 1

pragma solidity ^0.4.21;

contract Foo {
    function getRange(uint n) public pure returns(uint[]) {
        uint[] memory result = new uint[](n);
        for (uint i = 0; i < n; i++)
            result[i] = i;
        return result;
    }
}
2
  • 1
    Sorry, it was a bad example. Please, see edit. Commented Apr 26, 2018 at 13:11
  • still working for 0.8.11 lol
    – Rotem
    Commented Apr 13, 2022 at 13:03
2

as i know

push is only for storage arrays not memory arrays

from the doc :

push: Dynamic storage arrays and bytes (not string) have a member function called push that can be used to append an element at the end of the array. The function returns the new length.

try

result[j]=keccak256(id);//declare j
3
  • 2
    Isn't it out of bounds access? Or I should implement my own dynamic array resize? Commented Apr 26, 2018 at 11:33
  • 1
    no for array resizing here's the doc solidity.readthedocs.io/en/develop/…
    – Badr Bellaj
    Commented Apr 26, 2018 at 11:56
  • 1
    Okay. So I have manually implement list? Allocate some array. Fill it. When I have to push yet another item I should allocate new array, copy all items from the old one, replace old array with new one and finally add an item there? And when I'm done I should allocate yet another array a final result, and copy all items in it? I supposed there is some library (if language isn't support if out of the box). Please see updated question if I was unclear in comment. Commented Apr 26, 2018 at 12:11
-1

Since you have a condition in the iteration I will add this key in Henk's answer:

pragma solidity ^0.4.21;

contract Foo {
    function getRange(uint n) public pure returns(unit[] memory result) {
        uint j = 0; 
        for (uint i = 0; i < n; i++)
            if (someCondition(i))
                result[j] = i;
                j++;
        }
}
-1

Use storage instead of memory

pragma solidity ^0.4.21;

contract Foo {
    function getRange(uint n) public view returns(uint[]) {
        uint[] storage result;
        for (uint i = 0; i < n; i++)
            if (i> 1)
               result.push(i);
        return result;
    }
}
1
  • 1
    The code has a bug if there's a non empty uint[] in the first slot.
    – Ismael
    Commented Mar 17, 2022 at 4:39

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