Search code examples
pythonstringdictionaryexec

Using a string as a dictionary's index


I have a large dictionary (originally a json), something like this:

dictionary = {
    "Thing1": {
        "Thing1.1": {
            "Thing1.1.1": "Text",
            "Thing1.1.2": "Text",
            "Thing1.1.3": "Text"},
        "Thing1.2": [{
            "Thing1.2.1.1": "Text",
            "Thing1.2.1.2": "Text",
            "Thing1.2.1.3": "Text"},
            {
            "Thing1.2.2.1": "Text",
            "Thing1.2.2.2": "Text",
            "Thing1.2.2.3": "Text we're interested in"}
        ],
        "Thing1.3": {
            "Thing1.3.1": "Text",
            "Thing1.3.2": "Text",
            "Thing1.3.3": "Text"}
    },
    "Thing2": {
            "Thing2.1": {
                "Thing2.1.1": "Text",
                "Thing2.1.2": "Text",
                "Thing2.1.3": "Text"},
            "Thing2.2": {
                "Thing2.2.1": "Text",
                "Thing2.2.2": "Text",
                "Thing2.2.3": "Text"},
            "Thing2.3": {
                "Thing2.3.1": "Text",
                "Thing2.3.2": "Text",
                "Thing2.3.3": "Text"}
        },
    "Thing3": {
        "Thing3.1": {
            "Thing3.1.1": "Text",
            "Thing3.1.2": "Text",
            "Thing3.1.3": "Text"},
        "Thing3.2": {
            "Thing3.2.1": "Text",
            "Thing3.2.2": "Text",
            "Thing3.2.3": "Text"},
        "Thing3.3": {
            "Thing3.3.1": "Text",
            "Thing3.3.2": "Text",
            "Thing3.3.3": "Text"}
    }
}

And, having automated some things, I have some keys the values of which I'm interested in, in a string as such: key = '["Thing1"]["Thing1.2"][1]["Thing1.2.2.3"]'' and I'd like to be able to call the value for which I have the key as if it was the following: value = dictionary["Thing1"]["Thing1.2"][1]["Thing1.2.2.3"]

I have tried using exec, and formatting everything neatly using f-strings, however that's horrible and it does give me some trouble of its own, especially if I use it in functions due to the way it handles global and local variables.

Edit: I've changed the dictionary and key to represent what it looks more like in the real case: This is a dictionary of arbitrary depth that may contain lists embedded within it e.g. 'Thing1.2'.


Solution

  • Let's assume that the depth of the nested dictionaries is unknown. In that case we need a loop to navigate it. You can use a regular expression to isolate the embedded keys. Something like this:

    import re
    
    dictionary = {
        "Thing1": {
            "Thing1.1": {
                "Thing1.1.1": "Text",
                "Thing1.1.2": "Text",
                "Thing1.1.3": "Text"},
            "Thing1.2": [{
                "Thing1.2.1.1": "Text",
                "Thing1.2.1.2": "Text",
                "Thing1.2.1.3": "Text"},
                {
                "Thing1.2.2.1": "Text",
                "Thing1.2.2.2": "Text",
                "Thing1.2.2.3": "Text we're interested in"}
            ],
            "Thing1.3": {
                "Thing1.3.1": "Text",
                "Thing1.3.2": "Text",
                "Thing1.3.3": "Text"}
        },
        "Thing2": {
            "Thing2.1": {
                "Thing2.1.1": "Text",
                "Thing2.1.2": "Text",
                "Thing2.1.3": "Text"},
            "Thing2.2": {
                "Thing2.2.1": "Text",
                "Thing2.2.2": "Text",
                "Thing2.2.3": "Text"},
            "Thing2.3": {
                "Thing2.3.1": "Text",
                "Thing2.3.2": "Text",
                "Thing2.3.3": "Text"}
        },
        "Thing3": {
            "Thing3.1": {
                "Thing3.1.1": "Text",
                "Thing3.1.2": "Text",
                "Thing3.1.3": "Text"},
            "Thing3.2": {
                "Thing3.2.1": "Text",
                "Thing3.2.2": "Text",
                "Thing3.2.3": "Text"},
            "Thing3.3": {
                "Thing3.3.1": "Text",
                "Thing3.3.2": "Text",
                "Thing3.3.3": "Text"}
        }
    }
    
    key = '["Thing1"]["Thing1.2"][1]["Thing1.2.2.3"]'
    
    list_ = re.findall(r'\["*(.*?)"*\]', key)
    
    d = dictionary
    
    for k in list_:
        if k.isdigit() and isinstance(d, list):
            d = d[int(k)]
        else:
            d = d.get(k)
        if d is None:
            break
    
    print(d)
    

    Output:

    Text we're interested in