Adding missing keys in dictionary in Python

2019-03-30 09:57发布

问题:

I have a list of dictionaries:

L = [{0:1,1:7,2:3,4:8},{0:3,2:6},{1:2,4:6}....{0:2,3:2}]. 

As you can see, the dictionaries have different length. What I need is to add missing keys:values to every dictionary to make them being with the same length:

L1 = [{0:1,1:7,2:3,4:8},{0:3,1:0,2:6,3:0,4:0},{0:0, 1:2,3:0,4:6}....{0:2,1:0,2:0,3:2,4:0}], 

Means to add zeros for missing values. The maximum length isn't given in advance, so one may get it only iterating through the list.

I tried to make something with defaultdicts, like L1 = defaultdict(L) but it seems I don't understand properly how does it work.

回答1:

a bit of caution: changes L

>>> allkeys = frozenset().union(*L)
>>> for i in L:
...    for j in allkeys:
...        if j not in i:
...            i[j]=0

>>> L
[{0: 1, 1: 7, 2: 3, 3: 0, 4: 8}, {0: 3, 1: 0, 2: 6, 3: 0, 4: 0}, {0: 0, 1: 2, 2:
 0, 3: 0, 4: 6}, {0: 2, 1: 0, 2: 0, 3: 2, 4: 0}]


回答2:

You'll have to make two passes: 1 to get the union of all keys, and another to add the missing keys:

max_key = max(max(d) for d in L)
empty = dict.fromkeys(range(max_key + 1), 0)
L1 = [dict(empty, **d) for d in L]

This uses an 'empty' dictionary as a base to quickly produce all keys; a new copy of this dictionary plus an original dictionary produces the output you want.

Note that this assumes your keys are always sequential. If they are not, you can produce the union of all existing keys instead:

empty = dict.fromkeys(set().union(*L), 0)
L1 = [dict(empty, **d) for d in L]

Demo:

>>> L = [{0: 1, 1: 7, 2: 3, 4: 8}, {0: 3, 2: 6}, {1: 2, 4: 6}, {0: 2, 3: 2}]
>>> max_key = max(max(d) for d in L)
>>> empty = dict.fromkeys(range(max_key + 1), 0)
>>> [dict(empty, **d) for d in L]
[{0: 1, 1: 7, 2: 3, 3: 0, 4: 8}, {0: 3, 1: 0, 2: 6, 3: 0, 4: 0}, {0: 0, 1: 2, 2: 0, 3: 0, 4: 6}, {0: 2, 1: 0, 2: 0, 3: 2, 4: 0}]

or the set approach:

>>> empty = dict.fromkeys(set().union(*L), 0)
>>> [dict(empty, **d) for d in L]
[{0: 1, 1: 7, 2: 3, 3: 0, 4: 8}, {0: 3, 1: 0, 2: 6, 3: 0, 4: 0}, {0: 0, 1: 2, 2: 0, 3: 0, 4: 6}, {0: 2, 1: 0, 2: 0, 3: 2, 4: 0}]

The above approach to merge two dictionaries into a new one with dict(d1, **d2) always works in Python 2. In Python 3 additional constraints have been set on what kind of keys you can use this trick with; only string keys are allowed for the second dictionary. For this example, where you have numeric keys, you can use a union of their dictionary views instead:

dict(d.items() | empty.items())  # Python 3 dictionary merge


回答3:

This is only a solution, but I think it's simple and straightforward. Note that it modifies the dictionaries in place, so if you want them to be copied, let me know and I'll revise accordingly.

keys_seen = []
for D in L:  #loop through the list
    for key in D.keys():  #loop through each dictionary's keys
        if key not in keys_seen:  #if we haven't seen this key before, then...
            keys_seen.append(key)  #add it to the list of keys seen

for D1 in L:  #loop through the list again
    for key in keys_seen:  #loop through the list of keys that we've seen
        if key not in D1:  #if the dictionary is missing that key, then...
            D1[key] = 0  #add it and set it to 0


回答4:

Maybe not the most elegant solution, but should be working:

L = [{0:1,1:7,2:3,4:8},{0:3,2:6},{1:2,4:6},{0:2,3:2}]

alldicts = {}
for d in L:
    alldicts.update(d)

allkeys = alldicts.keys()

for d in L:
    for key in allkeys:
        if key not in d:
            d[key] = 0

print(L)


回答5:

Unless None is a valid value for a dictionary key you have herein is a great solution for you

L = [{0: 1, 1: 7, 2: 3, 4: 8}, {0: 3, 2: 6}, {1: 2, 4: 6}, {0: 2, 3: 2}]
for i0, d0 in enumerate(L[:-1]):
    for d1 in L[i0:]:
        _ = [d0.__setitem__(k,d1[k]) for k in d1 if d0.get(k,None) is None]
        _ = [d1.__setitem__(k,d0[k]) for k in d0 if d1.get(k,None) is None]

print(L)
>>> [{0: 1, 1: 7, 2: 3, 3: 2, 4: 8}, {0: 3, 1: 2, 2: 6, 3: 2, 4: 6}, {0: 2, 1: 2, 2: 3, 3: 2, 4: 6}, {0: 2, 1: 7, 2: 3, 3: 2, 4: 8}]