Populating a Nested Dictionary

2019-08-11 21:44发布

I have a long list of nested tuples that I am iterating through and appending in a certain way such that an empty dictionary:

dict = {}

will be filled like this:

dict = {a: {b:1,5,9,2,3}, b: {c:7,4,5,6,2,4}, c: {b:3,13,2,4,2}... }

The iteration will check if a nested dictionary exists, and if so, then it will append the value, otherwise, create a nested dictionary. My poor attempt looks something like this:

longlist = [(1,(a,b)),(2,(b,c)), (3,(c,b)) ... ]
dict = {}
    for each in longlist:
        if dict[each[1][0]][each[1][1]]:
            dict[each[1][0]][each[1][1]].append(each[0])
        else:
            dict[each[1][0]] = {}
            dict[each[1][0]][each[1][1]] = each[0]

The problem with my approach is that the iteration fails because the dictionary is empty to begin with or that the parent of the nest doesn't exist in dict. It's getting complicated for me. I wasn't able to find much info online on how to tackle nested dictionaries, so I thought it should be okay to ask here.

2条回答
Summer. ? 凉城
2楼-- · 2019-08-11 22:11

Here is a solution using collections.defaultdict

import random
import collections
choices = ['a', 'b', 'c', 'd', 'e', 'f']

longlist = []
for i in range(1, 101):
    longlist.append((i, tuple(random.sample(choices, 2))))

print longlist

final = collections.defaultdict(lambda: collections.defaultdict(list))

for value, (key1, key2) in longlist:
    final[key1][key2].append(value)


print final

In general, the way that I would change your code would be to first ensure the nested dictionaries exist (collections.defaultdict takes care of this for you) and then always append once.

Something like

for value (key1, key2) in longlist:
    if not your_dict.get(key1):
        your_dict[key1] = {}
    if not your_dict.get(key1).get(key2):
        your_dict[key1][key2] = []
    your_dict[key1][key2].append(value)

Also not the for line vs "for each ..." This is unpacking the items in the iterable. You could also have done

for value, keys in longlist:

but since keys is an iterable as well, you can unpack it as well if you wrap it in parens.

查看更多
forever°为你锁心
3楼-- · 2019-08-11 22:30

Without delving much into what you are trying to do, you can rewrite your if statement to not throw an error if the keys do not exist:

if dict_.get(each[1][0], {}).get(each[1][1], None):
    dict_[each[1][0]][each[1][1]].append(each[0])

The dict.get is an extremely useful function in that it returns a certain default value if the given key does not exist.

Also, it seems you expect a list to exist. In the else block, did you mean to do this?

dict_[each[1][0]][each[1][1]] = [each[0]]

This will create a list with a single element, so now dict[...][...].append(...) will work.

I also recommend not using dict to name your variable. It shadows the inbuilt class.

Further improvements might include unpacking items in the head of the for loop, so you don't have to do each[0], each[1], and so on. You could use something like:

for idx, pair in longlist:
    x, y = pair # unpack each pair now
    ...

Full listing:

dict_ = {}
for idx, pair in longlist:
    x, y = pair
    if dict_.get(x, {}).get(y, None):
        dict_[x][y].append(idx)

    else:
        dict_[x] = {y : [idx] }

This is much more readable than before.

查看更多
登录 后发表回答