Solidity: Returns filtered array of structs without 'push'

Viewed 3762

I have this contract with an array of structs:

pragma solidity ^0.6.0;
pragma experimental ABIEncoderV2;

contract Tickets {
  struct Ticket {
    uint id;
    int val;
  }

  Ticket[] tickets;

  function addTicket(uint id, int val) public returns(bool success) {
    Ticket memory newTicket;
    newTicket.id = id;
    newTicket.val = val;
    tickets.push(newTicket);

    return true;
  }

  function getTicket(uint id) public view returns(Ticket memory) {
    uint index;

    for(uint i = 0; i<tickets.length; i++){
      if (tickets[i].id == id) {
        index = i;
        break;
      }
    }

    Ticket memory t = tickets[index];

    return t;
  }

  function findTickets(int val) public view returns(Ticket[] memory) {
    Ticket[] memory result;

    for(uint i = 0; i<tickets.length; i++){
      if (tickets[i].val == val) {
        result.push(tickets[i]); // HERE IS THE ERROR
      }
    }

    return result;
  }
}

I need to returns a filtered by val array but when I buil this code: result.push(tickets[i].id); it throw this error:

TypeError: Member "push" is not available in struct Tickets.Ticket memory[] memory outside of storage.

How I can implement the filter without using push ?

1 Answers

Returning dynamic-length array of struct is still a bit tricky in Solidity (even in the current 0.8 version). So I made a little workaround to make it work even in the 0.6.

  1. Determine the result count, you'll need it for step 2.
  2. Create a fixed-length array
  3. Fill the fixed-length array
  4. Return the fixed-length array
function findTickets(int val) public view returns(Ticket[] memory) {
    uint256 resultCount;

    for (uint i = 0; i < tickets.length; i++) {
        if (tickets[i].val == val) {
            resultCount++;  // step 1 - determine the result count
        }
    }

    Ticket[] memory result = new Ticket[](resultCount);  // step 2 - create the fixed-length array
    uint256 j;

    for (uint i = 0; i < tickets.length; i++) {
        if (tickets[i].val == val) {
            result[j] = tickets[i];  // step 3 - fill the array
            j++;
        }
    }

    return result; // step 4 - return
}
Related