Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Solidity: Returns filtered array of structs without 'push'

Tags:

solidity

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 ?

like image 625
drinor Avatar asked Mar 10 '20 11:03

drinor


People also ask

How to declare an array in solidity?

To declare an array in Solidity, the data type of the elements and the number of elements should be specified. The size of the array must be a positive integer and data type should be a valid Solidity type

Why does the Getter return a struct in solidity?

It’s an array, which is how Solidity tuples are returned to Javascript land. Alright, so we know that the getter is not returning a struct, per se, but rather a tuple.

Is it possible to filter the entire array in a contract?

Also, depending on your use cases, you might want to have the contract return the entire array and do the filtering client-side. It really depends on what you are trying to achieve and how big / difficult to filter your data is. When reading data you are still limited to gas requirements, but the user does not pay for it.

Why does the Getter return a struct in JavaScript?

It’s an array, which is how Solidity tuples are returned to Javascript land. Alright, so we know that the getter is not returning a struct, per se, but rather a tuple. This is a big clue! When the compiler generates a getter for a mapping or an array where the element is a struct, it does something like the following:


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
}
like image 110
Petr Hejda Avatar answered Oct 12 '22 19:10

Petr Hejda