nested dictionary from lists

I have three lists (of lists) - index_1, index_2 and index_3. I want to combine them to make a nested dictionary. The keys of the top level of the dictionary should be index_1 = ['item1','item2','item3']. The second level should be keyed by index_2 (which has 3 elements corresponding to 'item1', 'item2', and 'item3'). The values in the second level of the dictionary should contain values and be stored in a list as in index_3.

I am a bit stuck and think I am creating overly complicated loops shown below. Any comments would be great.

# input lists
index_1 = ['item1', 'item2', 'item3']
index_2 = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
index_3 = [[0, 1, 2, 3, 4], [99, 100], [0, 1, 2]]

# desired output
my_dict = {'item1' : {'a' : [0, 1, 2, 3, 4], 'b' : [0, 1, 2, 3, 4], 'c' : [0, 1, 2, 3, 4]},
           'item2' : {'d' : [99, 100], 'e' : [99, 100], 'f' : [99, 100]},
           'item3' : {'g' : [0, 1, 2], 'h' : [0, 1, 2], 'i' : [0, 1, 2]}}

# my loop that didnt work
d = defaultdict(defaultdict)
for i in range(len(index_1)):
    for x, y, z in zip([index_1[i]], [index_2[i]], [index_3[i]]):
        d[x][y] = [z]

You can step along multiple lists in lockstep with zip, e.g.,

zip(index_1, index_2, index_3)

At each step, the first element is the key, and the next ones make the nested dictionary. The nested dictionary can be made by dict.fromkeys as long as you're ok with having the same reference in all the values:

d = {}
for k, nk, nv in zip(index_1, index_2, index_3):
    d[k] = dict.fromkeys(nk, nv)

Otherwise, you need to make a new list for each element:

d = {}
for k, nk, nv in zip(index_1, index_2, index_3):
    d[k] = {kk: list(nv) for kk in nk}

list(nv), nv[:] and nv.copy() are all idiomatic, and a great case of "exceptions prove the rule" for python's ideal of having one right way to do things.

You can now simplify the whole thing into a nested dictionary comprehension:

d = {k: {kk: list(nv) for kk in nk} for k, nk, nv in zip(index_1, index_2, index_3)}

You don't need defaultdict or that extra loop, you can just zip them together like below:

index_1 = ['item1', 'item2', 'item3']
index_2 = [['a','b','c'], ['d','e','f'], ['g','h','i']]
index_3 = [[0,1,2,3,4],[99,100], [0,1,2]]

dic = {}
for x, y, z in zip(index_1, index_2, index_3):
    dic[x] = {}
    for k in y:
        dic[x][k] = z[:]

The z[:] is to ensure a new list is created and the same reference isn't reused for all keys.


This code will still work if you want to use dic = defaultdict(defaultdict), the benefit being you can omit dic[x] = {} from your loop. It may make more sense to go with defaultdict(dict) there though, since there's no benefit to the nested ones being a defaultdict.


You can do:

index_1 = ['item1', 'item2', 'item3']
index_2 = [['a','b','c'], ['d','e','f'], ['g','h','i']]
index_3 = [[0,1,2,3,4],[99,100], [0,1,2]]

vals=zip(index_2, index_3)
di={}
for k1 in index_1:
    ct=next(vals)
    di[k1]={k:ct[1] for k in ct[0]}

If you want the individual lists inside to be copies, do:

di[k1]={k:list(ct[1]) for k in ct[0]}

Either case:

>>> di
{'item1': {'a': [0, 1, 2, 3, 4], 'b': [0, 1, 2, 3, 4], 'c': [0, 1, 2, 3, 4]}, 'item2': {'d': [99, 100], 'e': [99, 100], 'f': [99, 100]}, 'item3': {'g': [0, 1, 2], 'h': [0, 1, 2], 'i': [0, 1, 2]}}

Given your example output:

>>> di==my_dict
True