如何在Solidity中返回映射列表?(以太坊合约)

25

我想编写一个简单的智能合约,其中包含一个列表,可以设置项目并获取该列表。

使用Solidity编写如下:

contract lister {
    mapping(int => string) list;
    int id = 0;
    
    function getList() returns ( /*HERE*/ ) {
        return list;
    }

    function setItemToList(string str) {
        list[id] = str;
        id++;
    }
}

我想让getList()返回列表,但返回类型不兼容。我该怎么做?

4个回答

23

在Solidity中,批量访问列表/数组等非常麻烦。在合约中很少看到这种情况。对于您的情况,一种可能的解决方案是提供一个函数来访问一个项目,使用它的索引,并让调用者从0循环到id。


我相信这个问题的答案是肯定的。事实上,你可以看到哪些网站实现了这一点,因为它们可以在没有安装metamask的情况下访问。 - jlansey
1
我仍然在寻找答案,到目前为止,似乎唯一能够获取整个列表而无需执行for循环/速率限制的方法是处理交易/事件历史记录并自己存储数据...这意味着我需要始终保持自己的存储和区块链之间的数据同步... - xiao

8

在映射中,键值不会被存储,也无法迭代其中的值,因此它们只适用于单值查找。在您提供的示例中,使用数组可能是更好的选择。

另一方面,如果您使用数组并需要对其进行搜索(循环遍历所有项),则需要小心,因为如果数组中的项过多,则调用函数可能会消耗相当多的gas。


3
你可以改变你的变量list的可见性,插入public关键字,这样就可以通过getList访问它了。 mapping(int => string) public list;

2
你不应该这样做。这会使地图对任何人都可用。因此,他们可以在不知道键值的情况下访问地图。 - e.k
1
@e.k 看起来OP想让地图对任何人都可用。这个做法有其他不好的原因,还是只是想保持隐私而已? - dlsso
这能让任何人都能编辑吗? - Eslam Mahmoud

0

映射不存储它们的键,只存储在状态内存地址中存储的值。要获取数据列表,请使用数组。

address[] public addresses;

现在定义一个函数来获取这个数组的长度:
   function getAddressCount() public view returns(uint){
        return addresses.length;
    }

还要定义一个函数,通过索引获取元素:
function getAddressByIndex(uint index) public view returns(address){

   return addresses[index]
}

现在您需要编写代码逐个获取数组。以下是使用JavaScript和web3库完成此操作的方法:

let addresses,addressCount;
try {
     addressesCount = await ContractName.methods.getCampaignCounts().call();
       
    addresses = await Promise.all(
      Array(parseInt(addressesCount))
        .fill()
        .map((element, index) => {
          return ContractName.methods.getAddressByIndex(index).call();
        })
    );
    
  } catch (e) {
    console.log("error in pulling array list", e);
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接