Agree with everything @AdamAID said. Just to elaborate ...
Update:
There is a hidden assumption/confusion in the question. The example isn't valid.
This bit:
items["fruits"]["apples"] = "first apple";
items["fruits"]["apples"] = "second apple";
items["fruits"]["apples"] = "third apple";
That's just stomping on the same location/key three times; not storing as separate values as I imagine you want. Since you want fruits=>apples=>list of apples that exist, we need to structure the data a little differently.
Unless I miss my guess, you're going for three(?) one-to-many relationships, items has many categories (items are fruits, cars, houses ... ), categories has many species (categories are apples, plums, pairs...), and then there is a list of instances ... first apple, second apply, third apple ...
In a minimal implementation, you'll end up with an array in the mix:
items["fruits"]["apples"].instanceList[0] = "first apple";
items["fruits"]["apples"].instanceList[1] = "second apple";
items["fruits"]["apples"].instanceList[2] = "third apple";
I'm not sure I can do this justice in a small space, but you can find some explanation here: https://medium.com/@robhitchens/enforcing-referential-integrity-in-ethereum-smart-contracts-a9ab1427ff42
General idea; park a list of instances in each items[][]
location:
In this case, it is possible to destroy all apples with delete items["fruits"]["apples"];
or orphan them with items["fruits"]["apples"].instanceList.length=0;
It doesn't destroy the mapping. It doesn't really destroy the arrays stored inside the mapping. That's the structural stuff that doesn't go away. It does make the arrays stored in the mapped location empty, so no more apples.
Organizing the data so you can easily do what needs to be done is crucial.
Hope it helps.
Original Answer
You're defining a state variable (the mapping
). Not something you want to think about as a transient variable. It can be "empty" (so to speak) but it can't be destroyed. At most, you can delete a key/value pair with mappedThing[key]
.
Ethereum is an append-only system, so even a delete
operation is in fact a new transaction appended to the end of the chain (in a block). This changes the way we think about delete
. For example, it's not possible to actually remove the previous state. A determined adversary would be able to discover the previous value at a previous block even after the delete
has been applied.
There are some advantages to explicitly deleting mapped values because this informs others the data is no longer important (as opposed to writing a 0). Doing so is incentivized with negative gas cost.
Commonly, there is an index to mapped keys (because mappings themselves aren't iterable). Something like bytes32[] public keyList
.
There is a common approach to logical delete; simply set a bool
(e.g. isActive=false
) inside the mapped struct
. Under that approach, you don't really want to delete
the element. You'll just skip "deleted" records as you find them, e.g. Mapped Structs with Index.
A less common approach is to actually remove the key from the list. That will prevent uncontrolled growth of the list in the case that logical deletes are a common occurrence in your app, e.g. Mapped Structs with Delete-enabled Index
We can even imagine sorted indexes (e.g. linked list) and a logical splice operation that removes a key and thereby makes the mapped data inaccessible via the contract functions.
In summary, logical removal or records/objects may be sufficient:
- If you're relying on a flag to indicate "deleted" then you obviously
can't actually
delete
the element from the mapping (things would
break).
- If you're using a delete-capable index scheme then removal of a key from the index is sufficient. It's not necessary, but it's possible to also
delete
a key/value pair from
the mapping. Not possible to completely destroy the mapping.
Both approaches to logical delete are elaborated here. Are there well-solved and simple storage patterns for Solidity?
Hope it helps.