2012-05-07 42 views
2

我有一个像确保列表中的所有类型的字典具有相同键

[{'x': 42}, {'x': 23, 'y': 5}] 

使用词典列表,并希望确保所有类型的字典按键相同,与None值如果该键不存在在原始字典中。所以上面的列表应该变成

[{'x': 42, 'y': None}, {'x': 23, 'y': 5}] 

什么是最美丽和pythonic的方式来做到这一点?目前的做法:

keys = reduce(lambda k, l: k.union(set(l)), [d.keys() for d in my_list], set()) 
new_list = [dict.fromkeys(keys, None) for i in xrange(len(my_list))] 
for i, l in enumerate(my_list): 
    new_list[i].update(l) 

但特别是前两行似乎有点笨拙。想法?

+2

也许你应该使用命名的元组而不是字典。 – kennytm

+0

哪个字典是“原始的”? –

+0

没有*'原创'*我认为 – jamylak

回答

5
>>> from itertools import chain 
>>> l = [{'x': 42}, {'x': 23, 'y': 5}] 
>>> all_keys = set(chain.from_iterable(l)) 
>>> for d in l: 
     d.update((k,None) for k in all_keys-d.viewkeys()) 


>>> l 
[{'y': None, 'x': 42}, {'y': 5, 'x': 23}] 
+0

发电机有趣的嵌套!我认为我以前从来没有见过这样的建筑,而且老实说,我不太确定我是否理解它为什么有效......请注意详细说明一下。 –

+1

它与嵌套的for循环相同,从左至右阅读它:'for d in l:for k in d:k'。但是k从列表理解开始就处于左侧。所以它会通过每一个字典,然后通过该字典中的每个键。 – jamylak

+0

+1。此外,我懒得检查,但我想知道,如果k不在d)中,则对于all_keys中的k,d.update((k,None))将比'd.update((k,None) k in all_keys-d.viewkeys())'。 –

3

要做到这一点最简单的方法:

from itertools import chain 

dicts = [{'x': 42}, {'x': 23, 'y': 5}] 

keys = set(chain.from_iterable(dicts)) 
for item in dicts: 
    item.update({key: None for key in keys if key not in item}) 

给予我们:

[{'y': None, 'x': 42}, {'y': 5, 'x': 23}] 

我们做所有的按键一组中的所有词典,然后我们通过dict s的更新循环他们没有的任何值。

使用itertools.chain.from_iterable()将是做reduce(or_, [dict.keys() for dict in dicts])一种替代,使用functools.reduce()(在3.x中,在2.X内建的reduce())和operator.or_,虽然我觉得这是不太可读。完整

newdicts = [{key: item.get(key, None) for key in keys} for item in dicts] 
+0

是。链条“变平”。但是你需要从字典中取出键吗?如果你只是迭代字典,你会得到的密钥......即'set(chain({1:2},{3:4}))''set'([1,3])' –

+0

@ andrewcooke这是真的,改变了。 –

2

这将创建词典的新名单,他们都:

如果你想创建一个新的列表,而不是更新旧的,只需更换与循环键:

>>> import itertools as it 
>>> l = [{'x': 42}, {'x': 23, 'y': 5}] 
>>> all_keys = set(it.chain.from_iterable(l)) 
>>> [dict((k, a.get(k, None)) for k in all_keys) for a in l] 
[{'x': 42, 'y': None}, {'x': 23, 'y': 5}] 
相关问题