Dictionnary of Dictionnaries issue: {UInt32:{UInt32:String}}

Using a dictionnary containing dictionnaries as values ex {UInt32:{UInt32:String}} may be quite harmful

Please find the code below explaining the problem and a suggested solution

pub fun main() {

    let dictionnary:{UInt32:{UInt32:String}} = {}
    dictionnary[0] = {1 : "Value1", 2 : "Value2"}

    // Following code: Won't work and throw "cannot index into value which has type: `{UInt32: String}?`"
    let test2 = dictionnary[0][2]
    log(test2)

    // Following code: Won't work and throw "cannot index into value which has type: `{UInt32: String}?`"
    // Force Unwrap operator ! will not work here, has it is applied on second index in the inner dictionnary
    let test2 = dictionnary[0][2]!
    log(test2)

    // Following code: Will work but :
    // dictionnary[0] has to be fully copied to a new constant and unwrapped
    // may be time and resource consuming if dictionnary[0] contains many data
    let test1 = dictionnary[0]!
    log(test1[2])

    // Also for assiging new values you need to assing value to the unwrapped copy (test1) then assign the copy to the parent dictionnary

    // Conclusion
    // Using an inner  dictionnary in a dictionnary may be Harmful has it requires a copy to access/set its values
    // Proposed Solution
    // Update the operator unwrap ! to cascade unwrap
}

Thanks to Drapper Labs team

To solve this problem, I think Cadence should update its fundamental structures.
All the problem start from here: the values in dictionaries are all optionals.

Optional is a really powerful concept in Cadence, but it would be nice to have a new grammar something like !! which enables cascade unwrapping!

1 Like

let test2 = (dictionnary[0]!)[2]! is the way, but the problem is this looks ugly if you code ugly. Normally let’s imagine:

let metadatas:{UInt32:{UInt32:String}} = {}
var packID = 0 
var NAME = 1
var DESCRIPTION = 2 
metadatas[itemID] = {NAME : "Some Name", DESCRIPTION : "Some Description"}

it turns out:

fun getProperty(itemID, property): String{
    var metadata = metadatas[itemID] ?? panic("Item not found)
    var value = metadata[property] ?? panic("Item does not have the property)
    return value
}

I mean in my opinion, in most cases if you need cascade unwrapping you are writing ugly code.

Thank you! But I don’t know why your are calling ugly coding the need for using Dictionnary of Dictionnaries. This use is not a question of coding, but just a question of functional requirements … We are not all coding only for the simple metadata / PinataParty examples. Data structures representing Trees are more than basics in many fields. And very efficient for saving ressources and storage compared to linear collections.

I mean it is not ugly to use Dictionary of Dictionaries. even you can use Dictionary of Dictionary of Dictionaries.

Problem is accessing them:

dictionary[0][2][1] etc part is ugly.

I compared:

fun getProperty(itemID, property): String{
    var metadata = metadatas[itemID] ?? panic("Item not found)
    var value = metadata[property] ?? panic("Item does not have the property)
    return value
} 

ve

fun getProperty(itemID, property): String{
    return (metadatas[itemID]!)[property]!
} 

cadence runs like Exception based programming (with panics)

It is good practice to catch your exceptions and behave accordingly.

Instead of just throwing them.

Oh ok I do understand but you have here a paradox:

Here you don’t know about what will cause the exceptions but the code is very very efficient:

fun getProperty(itemID, property): String{
    return (metadatas[itemID]!)[property]!
} 

Here you know how to track the exception but your code needs to copy in memory all the content of metadatas[itemID], with your line here

var metadata = metadatas[itemID] ?? panic("Item not found)

if metadatas[itemID] contains a lot of entries, and yous metadatas a lot of Items it is a nightmare to execute, you can try, you will see a huge difference in the execution duration.

May be the best solution to let the operator ?? cascading through sub Dictionnaries

var metadata = metadatas[itemID][property] ?? panic("Item not found)

Actually, nothing is copied, unless you change [1]. Execution performance should be the same.

[1] Copy-on-write - Wikipedia