List 确保列表中的所有dict都有相同的键

List 确保列表中的所有dict都有相同的键,list,dictionary,set,python,List,Dictionary,Set,Python,我有一个字典列表,比如 [{'x': 42}, {'x': 23, 'y': 5}] 并希望确保所有dict具有相同的键,如果原始dict中不存在键,则值为None。因此,上面的列表应为 [{'x': 42, 'y': None}, {'x': 23, 'y': 5}] 做这件事最漂亮、最有魅力的方式是什么?目前的做法: keys = reduce(lambda k, l: k.union(set(l)), [d.keys() for d in my_list], set()) new_li

我有一个字典列表,比如

[{'x': 42}, {'x': 23, 'y': 5}]
并希望确保所有dict具有相同的键,如果原始dict中不存在键,则值为
None
。因此,上面的列表应为

[{'x': 42, 'y': None}, {'x': 23, 'y': 5}]
做这件事最漂亮、最有魅力的方式是什么?目前的做法:

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)

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

最简单的方法是:

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()
的替代方法是使用
functools.reduce()
(在3.x中,2.x中内置
reduce()
)和
操作符或
,尽管我觉得可读性较差

如果要创建新列表,而不是更新旧列表,只需将for循环替换为:

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

这将创建一个新的字典列表,所有字典都有完整的键:

>>> 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}]

也许你应该使用命名元组而不是字典。哪个dict是“原始的”?没有“原始的”ithink@Nkosinathi:对原始dict使用
defaultdict(lambda:None)
可能会解决问题。是的。链条是“扁平的”。但是你需要从字典里取钥匙吗?如果你只是在dict上迭代,你会得到键。。。ie
set(chain({1:2},{3:4}))
给出了
set([1,3])
@andrewcooke这是真的,改变了。有趣的生成器嵌套!我想我以前从未见过这种结构,老实说,我不太确定我是否理解它为什么会起作用。。。需要详细说明吗?它与嵌套for循环相同,从左到右阅读它,如:
对于l中的d:对于d中的k:k
。但是k在左边,因为它是一个列表。所以它会遍历l中的每一个字典,然后遍历该字典中的每一个键。另外,我也懒得检查,但我想知道
d.update((k,None)for k in all_key如果k不在d中)
是否比
d.update((k,None)for k in all_key-d.viewkeys())
。不错,尽管
chain
似乎更快(列表中有约3000个dict,每个都有约10个键):
%timeit set设置(k for d in l for k in m)
1000个循环,每个循环的最佳时间为3:4.86毫秒
%timeit set(chain.from_iterable(l))
1000个循环,每个循环的最佳时间为3:2.61毫秒
@Nkosinathi我将其更改为
链。从
,您是正确的,它会好得多。
>>> 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}]