简体   繁体   中英

varying depth nested dict from list of tuples

Problem statement

I aim at remapping the keys (essentially by swapping them) of a nested dictionary with varying depth, by using a mapping list.

My approach is:

  1. flatten the dictionary and turn it into a list of tuples,
  2. rebuild the dictionary and swap the keys by using a mapping list.

I was able to do #1, I am looking for a fine solution for #2. Generally speaking, I would love the solution to be as general as possible.

Specific case

I have a N to N-1 level nested dictionary, where the first three levels needs to be swapped given a mapping. A chunk of my dictionary looks like this:

raw_dict = {'a':{'b':{'c':[1,2,3]}},
            'd':{'e':{'f':{'g':[4,5,6]}}}}

I managed to flatten the raw dictionary to a list of tuples with recursion, ie with the following function

def unnest(d, keys=[]):
    '''
    convert a dictionary to a (ordered) list of tuples
    input: dictionary
    output: list of tuples
    '''
    lt = []
    for k, v in d.items():
        if isinstance(v, dict):
            lt.extend(unnest(v, keys + [k]))
        else:
            lt.append(tuple(keys + [k, v]))
    return lt

Now, I have a list of tuples with varying length, but the longest element is always 1 element longer than the shortest (basically every tuple has either N or N-1 elements). Given raw_dict from the example above, the resulting list of tuples would be like this

reform_list = [('a','b','c',[1,2,3]),('d','e','f','g',[4,5,6])]

where the first has length 4 (N-1) and the second has length 5 (N).

What I would like to achieve...

...is to build a function that builds a nested dictionary based on the tuple, regardless of the length, but based on a mapping like the following:

my_map = [2,1,0,3]

The function would be something like

def nest(my_tuple,my_map):
    [do something]
    return my_dict

I would call the function in a loop like this

for t in reform_list:
    n_d = nest(t,my_map)
    print(n_d) 

The expected outcome of the loop would be:

{'c':{'b':{'a':[1,2,3]}}}
{'f':{'e':{'d':{'g':[4,5,6]}}}}

In the last step...

...I will build the swapped dictionary by updating it, regardless of the depth, with recursion, starting from the single ones generated in the "wanted" function by using the following function

def update(d, u):
    '''
    update nested dictionary
    input: dict to update (d), update to use (u)
    output: updated dict (d)
    '''
    for k, v in u.items():
        if isinstance(v, collections.abc.Mapping):
            d[k] = update(d.get(k, {}), v)
        else:
            d[k] = v
    return d

You can use recursion:

reform_list = [('a','b','c',[1,2,3]),('d','e','f','g',[4,5,6])]
my_map = [2,1,0,3] 
def build_dict(mp, val):
  if not mp or len(val) - 1 <= mp[0]:
     return val[-1]
  return {val[mp[0]]:build_dict(mp[1:],val)}

results = [build_dict(my_map, i) for i in reform_list]

Output:

[{'c': {'b': {'a': [1, 2, 3]}}}, {'f': {'e': {'d': {'g': [4, 5, 6]}}}}]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM