Daniel Ferreira Jorge
Daniel Ferreira Jorge

Reputation: 387

Generic way to create nested dictionary from flat dictionary in python

I'm looking for a generic way to convert a flat dict like this:

{
    "det_0_prod_di_0_field" : "value", 
    "det_0_prod_di_0_adi_0_field" : "value", 
    "det_0_prod_di_0_adi_1_field" : "value", 
    "det_1_prod_di_0_field" : "value", 
    "det_1_prod_di_0_adi_0_field" : "value", 
    "det_1_prod_di_0_adi_1_field" : "value", 
}

Into this:

[
    {
        "det" : {
            "prod" : [{
                "di" : {
                    "field" : "value", 
                    "adi" : [{"field" : "value"}, {"field" : "value"}]
                }, 
            }]
        }, 
        "det" : {
            "prod" : [{
                "di" : {
                    "field" : "value", 
                    "adi" : [{"field" : "value"}, {"field" : "value"}]
                }, 
            }]
        }
    }
]

Notice that I have to create lists of dict for items that "repeat"... Also, It needs to be generic, because it can have many "levels of nesting"... every time "_0", "_1" etc appears in a key, a list should be created...

Thanks!

Upvotes: 0

Views: 749

Answers (2)

deets
deets

Reputation: 6395

You might consider following and using then formencode's approach of serializing structures to flat names and then reading them back in.

http://www.formencode.org/en/latest/modules/variabledecode.html#module-formencode.variabledecode

Upvotes: 1

abarnert
abarnert

Reputation: 365707

The first step is obviously to split each key into components:

target = {}
for key, value in source.items():
    components = key.split('_')

Now, what do you do with those components? Walk each one from left to right, keeping track of the current collection and the index or key to the next one, creating any missing sub-collections along the way.

You haven't specified your rule for what counts as an index vs. a key, what happens if indices are skipped or appear out of order, what happens if you get both an index and a key under the same prefix, etc., so I'll just pick some arbitrary rules for each—just treat every component as a key, because that makes for the shortest code. That's obviously not the rule you want, but if you understand the code, you should be able to adapt it as appropriate; if not, you need to understand it before you try to use it.

    subtarget = target
    for component in components[:-1]:
        subtarget = subtarget.setdefault(component, dict())
    subtarget[components[-1]] = value

If you understand the code but don't know how to adapt it, you probably have to look up the int function, the try statement, or the list.insert method.

Upvotes: 2

Related Questions