Solidity
Solidity中從數組中刪除元素的有效方法
對於每個使用者,我想保留一組持有的資產(每個資產都有一個 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--; } }
希望能幫助到你。