Solidity

Solidity中從數組中刪除元素的有效方法

  • February 4, 2021

對於每個使用者,我想保留一組持有的資產(每個資產都有一個 ID)。

到目前為止,我的解決方案是:

struct User {
       uint userId;
       uint[] assets;
   }

對於每個asset使用者持有我推送到使用者的ID數組asset

我想給使用者一個選項來刪除一個asset.

對此最有效的方法是什麼?

為每個使用者分配所有可用assets的資源(考慮到您有很多可用資產,這將是非常浪費的)VS. assets每次他想刪除 an時迭代他的所有asset內容,在數組中找到它,然後從中刪除它並相應地移動所有數組 - 而且,此程式碼有點可怕:

function deleteAsset(uint assetId) external returns(uint) {
       bool assetFound = false;
       // Check if the asset found is the last asset (or we go out of boundaries)
       if (allUsers[msg.sender].assets[allUsers[msg.sender].assets.length - 1] == assetId){
           assetFound = true;
       }

       else{
           // Iterate over all user assets and find its index
           for (uint i = 0; i < allUsers[msg.sender].assets.length - 1; i++) {
               if (!assetFound && allUsers[msg.sender].assets[i] == assetId)
                   assetFound = true;

               if(assetFound)
                   allUsers[msg.sender].assets[i] = allUsers[msg.sender].assets[i + 1];
           }
       }

       if (assetFound){
           delete allUsers[msg.sender].assets[allUsers[msg.sender].assets.length - 1];
           allUsers[msg.sender].assets.length--;
       }
   }

如果我可以mapping為每個使用者保存一個表明他擁有什麼資產的值會容易得多,但是你不能mapping從函式中返回一個,而且我不知道view函式的基準和“蠻力”所有資產我假設每個使用者都可以使用它可能需要很多時間。

來源:https ://github.com/su-squares/ethereum-contract/blob/master/contracts/SuNFT.sol

幹得好:

算法:

uint[] assets;
mapping(uint=>uint) indexOfAsset;

function removeAssetFromArray(uint _assetToDelete) {
 uint index = indexOfAsset[_assetToDelete];
 if (!index) return;

 if (assets.length > 1) {
   assets[index] = assets[assets.length-1];
 }
 assets.length--; // Implicitly recovers gas from last element storage
}

警告:這種方法假設一個有序集合,而不是一個數組。換句話說,它假定數組沒有重複項。

通常,通過將列表中的最後一項移動到行中來刪除。

struct在 OP 的程式碼中從使用者處工作。

pragma solidity 0.5.1;

contract DeleteUser {

   struct UserStruct {
       uint userId;
       uint[] assets;
   }

   mapping(address => UserStruct) public userStructs;

   function deleteUserAsset(address user, uint assetIndex, uint asset) public {
       UserStruct storage u = userStructs[user];
       require(u.assets.length > assetIndex);
       require(u.assets[assetIndex] == asset); // this is a sanity check in case the list was re-ordered
       u.assets[assetIndex] = u.assets[u.assets.length-1];
       u.assets.length--;
   }

}

由於問題的措辭並不嚴格需要完整性檢查,但該函式依賴於呼叫者知道要刪除哪一行。deleteUserAsset()如果呼叫函式重新組織列表,這可能是一個真正的問題。為了安全起見,一個簡單的解決方法是添加該檢查。

在我看來,更好的是不要依賴呼叫者知道這一點。為此,我們需要一步查找來找到特定鍵所在的行。將需要另一個 32 字節的字來儲存它。

pragma solidity 0.5.1;

contract DeleteUser {

   struct UserStruct {
       bytes32[] assets;
       mapping(bytes32 => uint) assetPointers;
   }

   mapping(address => UserStruct) userStructs;

   function isUserAsset(address user, bytes32 assetId) public view returns(bool isIndeed) {
       if(userStructs[user].assets.length == 0) return false;
       return userStructs[user].assets[userStructs[user].assetPointers[assetId]] == assetId;
   }

   function deleteUserAsset(address user, bytes32 assetId) public {
       UserStruct storage u = userStructs[user];
       require(isUserAsset(user, assetId));
       uint rowToDelete = u.assetPointers[assetId];
       u.assets[rowToDelete] = u.assets[u.assets.length-1];
       u.assets.length--;
   }

}

希望能幫助到你。

引用自:https://ethereum.stackexchange.com/questions/35790