3

我需要能够在映射中的同一个键下有几个可能的值。今天,Solidity 的映射是单值的:写入一个值会覆盖前一个值(它仍在区块链中,但不能通过合约检索)。我编写了这段代码以获得多值映射:

contract MVM {

  struct Bucket {
    bool exists;
    uint num; // Never decreases: we can only add records, not remove them.
    mapping(uint => Record) records;
  }

  struct Record {
    bool exists;
    string info;
  }

  // Do not make it public: the compiler crashes when generating
  // accessors https://github.com/ethereum/solidity/issues/633
  mapping(string => Bucket) data;

  function set(string key, string value) {
    if (data[key].exists) {
      data[key].records[data[key].num] = Record(true, value);  
    }
    else {
      data[key].exists = true;
      data[key].records[0] = Record(true, value);
    }
    data[key].num++;
  }

  function num_of(string key) returns (uint) {
    return data[key].num; // Guaranteed to be initialized as zero?
  }

  function get(string key, uint index) returns (string) {
    if (!data[key].exists || !data[key].records[index].exists) {
      throw;
    }
    return data[key].records[index].info;
  }

}

从 geth 控制台使用它的一个例子:

 > mvs.set.sendTransaction("foo", "bar", {from:eth.accounts[0], gas: 1000000})
"0x79c52c437a94f3301775acec5639404eff563fce1a99ad097f5db28f109d7ab5"

> mvm.set.sendTransaction("foo", "thing", {from:eth.accounts[0], gas: 1000000})
"0xb26b8d34691b0da5cb48af68933e81b514199f4ed8bd2b557767c8b55da85f50"
> mvm.get.call("foo")
"bar"
> mvm.get.call("foo", 1)
"thing"
> mvm.num_of.call("foo")
2

我的方法有缺陷吗?还是更好的解决方案?

4

1 回答 1

4
contract MVM {

  struct Record {
    bool exists;
    string info;
  }

  mapping(string => Record[]) data;

  // If you want to iterate the whole thing, you can use this:
  string[] keysNames;

  function set(string key, string value) {
      // Remove this if, if you don't need iteration
      if (data[key].length == 0) {
          keysNames.push(key);
      }

      data[key].push(Record(true, value));
  }

  function num_of(string key) constant returns (uint) {
    return data[key].length;
  }

  function get(string key, uint index) constant returns (string) {
    if (data[key][index].exists == false) {
      throw;
    }
    return data[key][index].info;
  }

  function exampleIterate() constant returns (string last) {
      uint keysLen = keysNames.length;

      for(uint i = 0; i < keysLen; i++) {
        uint recordsLen = data[keysNames[i]].length;

        for(uint j = 0; j < recordsLen; j++) {          
          last = data[keysNames[i]][j].info;
        }
      }
  }

}
于 2016-07-22T10:10:25.797 回答