Каков наиболее эффективный способ сопоставления двух разных типов ключей со структурой?

В приведенном здесь коде я создал нового клиента с именем «mcycleowner» и сопоставил его с входным _address. Однако я также хочу сопоставить его с входной _license, чтобы информацию о клиенте можно было запрашивать либо с помощью Eth-адреса, либо с номерного знака.

Для этого я создал нового клиента «mcyclelicenses», и функция addLicense принимает те же входные данные, что и newMcycleOwner (кроме адреса), только этот клиент сопоставляется с _license. Эта функция запускается внутри функции «newMcycleOwner», так что и клиенты, и сопоставления создаются одновременно.

Однако такой способ означает наличие двух записей информации о каждом клиенте, что кажется пустой тратой места, а также всякий раз, когда необходимо изменить информацию о клиенте, ее придется изменить в двух местах. Есть лучший способ сделать это?

// < ---------------------- MOTORCYLCES ---------------------- >


mapping (address => Customer) mcycleowners;
address[] public mcycleownerAdds;


function newMcycleOwner(address _address, string _custname, string _color, string _make, uint32 _year, string _license) onlyMember public {

    Customer storage mcycleowner = mcycleowners[_address];
        mcycleowner.custname = _custname;
        mcycleowner.color = _color;
        mcycleowner.make = _make;
        mcycleowner.year = _year;
        mcycleowner.license = _license;

        mcycleownerAdds.push(_address);

    addLicense(_custname, _color, _make, _year, _license);
}

    function getMcycleAdds() public view returns(address[]) {
        return mcycleownerAdds;
    }

    function getMcycleByAdd(address _address) public view returns(string, string) {
        return (mcycleowners[_address].custname, mcycleowners[_address].license);
    }

    function countMcycle() public view returns(uint) {
        return mcycleownerAdds.length;
    }


// <---------- CREATION OF NEW MAP SO WE CAN QUERY BY LICENSE ---------->

//This seems to create two instances of each customer.  Replace with a better method.

mapping (string => Customer) mcyclelicenses;

function addLicense(string _custname, string _color, string _make, uint32 _year, string _license) onlyMember public {
    Customer storage mcyclelicense = mcyclelicenses[_license];
        mcyclelicense.custname = _custname;
        mcyclelicense.color = _color;
        mcyclelicense.make = _make;
        mcyclelicense.year = _year;

}

 function getMcycleByLic(string _license) public view returns(string, string, string, uint32) {
    return (mcyclelicenses[_license].custname, mcyclelicenses[_license].color, mcyclelicenses[_license].make, mcyclelicenses[_license].year);
}

Ответы (2)

Простым способом сделать это может быть сохранение информации о клиентах внутри массива и использование сопоставления для хранения индексов массива, чтобы вы могли напрямую обращаться к индексу через сопоставление и использовать этот индекс для прямого доступа к массиву.

Вот пример

pragma solidity ^0.4.23;

contract ContractX {
    struct Customer {
        string name;
        string license;
        /* other infos */
    }

    mapping (address => uint) addressIndexes;
    mapping (string => uint) licenseIndexes;
    Customer[] customers;
    uint customerCount;

    function addNewItem(address _address, string _name, string _license) public {
        customers.push(Customer(_name, _license));

        addressIndexes[_address] = customerCount;
        licenseIndexes[_license] = customerCount;

        customerCount++;
    }

    function getByAddress(address _address) public view returns(string, string) {
        return (
            customers[addressIndexes[_address]].name, 
            customers[addressIndexes[_address]].license
        );
    }

    function getByLicense(string _license) public view returns(string, string) {
        return (
            customers[licenseIndexes[_license]].name, 
            customers[licenseIndexes[_license]].license
        );
    }
}

Таким образом, информация о клиенте была сохранена только один раз, и вы отслеживаете только индекс массива в сопоставлении, чтобы впоследствии использовать его для прямого доступа к нужному вам клиенту.

Другим решением может быть использование Customerв качестве субконтракта, затем создание контракта при добавлении информации о клиенте и сохранение адреса этого нового контракта в ваших сопоставлениях. Но это открывает другой сценарий, и вам может потребоваться внести несколько изменений.

Ах, это здорово. Будучи еще совсем новичком в Solidity, я еще не понял, что могу возвращать определенные элементы из массива (только то, что я мог возвращать весь массив или длину массива). Я буду пробовать это. Спасибо!

Вы можете просто сопоставить лицензию с адресом, а затем выполнить два поиска, чтобы разыменовать лицензию.

mapping(string => address) mcyclelicences;

function getMcycleByLic(string _license) public view returns(string, string, string, uint32) { 
  return (
    mcycleowners[mcyclelicenses[_license]].custname,
    mcycleowners[mcyclelicenses[_license]].color,
    mcycleowners[mcyclelicenses[_license]].make,
    mcycleowners[mcyclelicenses[_license]].year
  );      
}
Это довольно интересно и кажется отличным коротким путем. Я буду пробовать оба в ближайшее время. Спасибо!