Fixed question:

I have a mapping:

mapping (bytes32 => mapping (bytes32 => bytes32[])) items;

With some elements in it:

items["fruits"]["apples"].push("first apple");
items["fruits"]["apples"].push("second apple");
items["fruits"]["apples"].push("third apple");
items["fruits"]["plums"].push("first plum");
items["vegetables"]["carrots"].push("first carrot");

I want to delete all fruits. The code below does not work:

delete items["fruits"]

The error I get is:

Error: Unary operator delete cannot be applied to type mapping(bytes32 => bytes32)

Original question:

I have a mapping:

mapping (bytes32 => mapping (bytes32 => bytes32)) items;

With some elements in it:

items["fruits"]["apples"] = "first apple";
items["fruits"]["apples"] = "second apple";
items["fruits"]["apples"] = "third apple";
items["fruits"]["plums"] = "first plum";
items["vegetables"]["carrot"] = "first carrot";

I want to delete all fruits. The code below does not work:

delete items["fruits"]

The error I get is:

Error: Unary operator delete cannot be applied to type mapping(bytes32 => bytes32)
up vote 10 down vote accepted

Unfortunately you can't delete a mapping. From the solidity docs at: http://solidity.readthedocs.io/en/develop/types.html

delete has no effect on whole mappings (as the keys of mappings may be arbitrary and are generally unknown). So if you delete a struct, it will reset all members that are not mappings and also recurse into the members unless they are mappings. However, individual keys and what they map to can be deleted.

So you could do something like delete items[i][j] but you would need to know i and j in advance.

One common pattern is to keep an additional array (e.g. bytes32[]) into which you store the keys to your mapping, which you could then iterate over. Or in your case you could keep a mapping from "fruits" to an array of fruits, and then iterate over this when you want to delete all fruits. You would need to maintain these secondary data structures as you add fruits to your main items mapping.

Hope this helps!

  • That's exactly what I was doing, thank you for answering and confirming it's the right way indeed. – Edward Ruchevits Apr 26 '17 at 21:38
  • how much gas could be expected for such an operation? any kind of estimation? – R.P. Carson Aug 29 '17 at 21:54
  • There is a workaround for deleting a mapping. – William Entriken Mar 19 at 3:54

All problems in computer science can be solved by another level of indirection.

enter image description here

pragma solidity ^0.4.20;

contract DeletableMapping {
    // Input is keccak256(uint32 mappingVersion, string carVIN)
    mapping (bytes32 => string) carfaxReports;
    uint32 currentMappingVersion;

    function getCarfaxReport(string _carVIN) external view returns(string) {
        bytes32 key = keccak256(currentMappingVersion, _carVIN);
        return carfaxReports[key];
    }

    function setCarfaxReport(string _carVIN, string _reportJSON) external {
        bytes32 key = keccak256(currentMappingVersion, _carVIN);
        carfaxReports[key] = _reportJSON;
    }

    function deleteAllReports() external {
        currentMappingVersion++;
    }

    function recoverGas(uint32 _version, string _carVIN) external {
        require(_version < currentMappingVersion);
        bytes32 key = keccak256(_version, _carVIN);
        delete(carfaxReports[key]);
    }
}

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:

  1. 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).
  2. 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.

  • Thank you, that's very useful! Yes, I understand that what's published, stays there forever, by "deleting" I meant wiping references, so the data won't be available for retrieval from the smart contract. – Edward Ruchevits Apr 26 '17 at 21:42
  • So, essentially, items["fruits"] is an element in items mapping, which itself references a mapping data structure. I want to "disable" retrieving that nested mapping data structure using the items["fruits"] key. – Edward Ruchevits Apr 26 '17 at 21:49
  • 1
    I understand what you're saying about the nested mappings. The namespace for items exists and cannot be destroyed even though possibly no values have been set (sort of empty). Return values always default to zero-ish. At any given first-level key there is another mapping. It's also a namespace that can't be destroyed. The state variables are a lot like defining a schema/organization structure. Those mappings can't be destroyed. Specific key/value pairs stored there can be deleted. Hope I'm not adding confusion :-) – Rob Hitchens B9lab Apr 26 '17 at 22:11
  • 1
    It would be prudent to try and delete data that is no longer used. This has an effect on the size of on disk storage for lite and slim clients. – o0ragman0o Apr 26 '17 at 23:48
  • 1
    The referential integrity idea uses key lists in addition to mapped structs, so you would have of such things. The destruction of a category would be accomplished by making the list length 0 or deleting the mapped struct. You can use the pattern recursively to represent all the one-to-many joins. The "many" related items can be deleted en masse if you make a function to do that. Something like delete all child things related to parent where the parent instances contain a list of related children. – Rob Hitchens B9lab Apr 26 '17 at 23:50

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.