5

I have a shop owner and a shop owner can have multiple products. I want to keep the products id in an array.

Also, A public method can return all the product id list of a shop owner.

I tried the following code

pragma solidity 0.5.0;

contract Shop {

    struct ShopOwner {
        string id;
        string[] productList;
    }

    struct Product {
        string id;
        string name;
    }

    mapping(string => ShopOwner) private mapShopOwner;

    function getUserProductList(string _shopOwnerId) public view returns(string[]) {
        return (mapShopOwner[_shopOwnerId].productList);
    }

}

This seems does not support the solidity language.

I am getting the error

browser/Structure.sol:17:33: TypeError: Data location must be "memory" for parameter in function, but none was given. function getUserProductList(string _userId) public view returns(string[]) { ^------------^

and

browser/Structure.sol:17:69: TypeError: Data location must be "memory" for return parameter in function, but none was given. function getUserProductList(string _userId) public view returns(string[]) { ^------^

And I do not want to use pragma experimental ABIEncoderV2;

I wonder, what is the correct procedure to handle this types of relation.

3
  • You are using many strings that is not a best practice for smart contracts.
    – Aniket
    Nov 28, 2018 at 5:01
  • Should I use bytes32, instead of string? Nov 28, 2018 at 5:05
  • 1
    Yes. String consumes more gas. Bytes32 is better if it suits your usecase
    – Aniket
    Nov 28, 2018 at 5:08

3 Answers 3

2

You can't return an array of strings at this time unless you use the experimental ABI encoder (which you should not use for production). We need to work around that.

You should use fixed-length types instead of string wherever possible. bytes32 and address are good choices.

A Solidity CRUD pattern doesn't store identifiers in structs. They are moved to a list to enable enumeration. Mapped structs store other data. https://medium.com/robhitchens/solidity-crud-part-1-824ffa69509a

If the shopOwnerIds are refactored as bytes32 then you can return a dynamic array of them, from a compiler perspective. There is a big caveat - it will not scale and it may become impractically expensive or even impossible to run. The serialize/deserialize idea also has this limitation.

So, unless you are certain your array size has an acceptable upper bound, you should not rely on dynamic arrays in function signatures.

More info about this danger: https://blog.b9lab.com/getting-loopy-with-solidity-1d51794622ad

A solution is to identify the iterative process in your contract (e.g. return list) and refactor it to push iterative processes out to the client. This is accomplished by returning a single instance/record/row at a time and (optionally) another function to return the count.

pragma solidity 0.5.0;

contract Shop {

    struct ShopOwner {
        string name;
        bytes32[] productList;
    }

    address[] public shopOwnerIds;
    mapping(address => ShopOwner) public shopOwners;

    struct Product {
        string name;
    }

    function shopCount() public view returns(uint) { return shopOwnerIds.length; }
    function shopProductCount(address shopOwnerId) public view returns(uint) { return shopOwners[shopOwnerId].productList.length; }

    function shopOwnerProductIdAtIndex(address shopOwnerId, uint row) public view returns(bytes32) {
        return shopOwners[shopOwnerId].productList[row];
    }
}

To really see that in action you will also need to functions to populate the tables. Those are left out to keep it close to the OP's version.

Consider simple functions to append shopOwners and to append products to shopOwners.

Hope it helps.

1

Solidity cannot return string arrays in current version. One workaround is to serialize the string array and then de-serialize it on client side.

An example :

https://hackernoon.com/serializing-string-arrays-in-solidity-db4b6037e520

1

Compiler complains about missing data location specifier in return type, should be returns (string [] memory). Also, returning string array as a whole is pobably bad idea because there could be use cases where only the length of array or only certain elements are needed. Common pattern is to have two methods: one returns array length and another returns array element by index.

Your Answer

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

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