从字典列表中提取所有键

64

我正在尝试获取一个字典列表中所有键的列表,以便填写csv.DictWriter的fieldnames参数。

之前,我有这样一段代码:

[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]

我之前使用了fieldnames = list[0].keys()来获取列表中第一个字典的键。

现在,我的列表中有一个字典比其他字典拥有更多的键值对(可以是任何一个结果)。 根据从API获取的信息,新键会动态添加到字典中,因此可能不会在每个字典中出现,而且我事先不知道会有多少个新键。

[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5, "height":4},
{"name": "Pam", "age": 7}
]

我不能仅使用fieldnames = list[1].keys(),因为不一定是第二个元素会有额外的键。

一个简单的解决方案是找到具有最多键的字典并将其用作字段名,但如果您有像这样的示例:

[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5, "height":4},
{"name": "Pam", "age": 7, "weight":90}
]

第二个和第三个字典都有3个键,但最终结果应该是列表["name", "age", "height", "weight"]

7个回答

101
all_keys = set().union(*(d.keys() for d in mylist))

编辑:必须解压缩列表。现在已修复。


1
这个解决方案完美地工作,但它似乎生成的键列表与它们被提取的字典列表有不同的顺序。有什么办法可以保持索引吗?谢谢! - Momchill
@Momchill 的顺序不能保证,因为他正在使用一个集合。我将在下面为您发布一个使用列表的代码片段。 - mareoraft

34

您的数据:

>>> LoD
[{'age': 10, 'name': 'Tom'}, 
 {'age': 5, 'name': 'Mark', 'height': 4}, 
 {'age': 7, 'name': 'Pam', 'weight': 90}]

这个集合理解将会完成它:
>>> {k for d in LoD for k in d.keys()}
{'age', 'name', 'weight', 'height'}

这是它的工作原理。首先,创建一个字典键的列表的列表。
>>> [list(d.keys()) for d in LoD]
[['age', 'name'], ['age', 'name', 'height'], ['age', 'name', 'weight']]

然后创建一个扁平化的列表版本。
>>> [i for s in [d.keys() for d in LoD] for i in s]
['age', 'name', 'age', 'name', 'height', 'age', 'name', 'weight']

创建一个集合来消除重复项:
>>> set([i for s in [d.keys() for d in LoD] for i in s])
{'age', 'name', 'weight', 'height'}

可以简化为:
{k for d in LoD for k in d.keys()}

如果您希望保持字典列表中键的初始顺序,可以使用字典而不是集合来生成非重复项。自Python 3.6起,字典保持插入顺序,而集合则不保持。
您可以这样做:
>>> list({k:None for d in LoD for k in d.keys()}.keys())
['age', 'name', 'height', 'weight']

或者,
>>> [k for k in {k:None for d in LoD for k in d}]
['age', 'name', 'height', 'weight']

5
from itertools import chain

lis = [
    {"name": "Tom", "age": 10},
    {"name": "Mark", "age": 5, "height":4},
    {"name": "Pam", "age": 7, "weight":90}
]

# without qualification a dict iterates over its keys
# and set takes any iterable in its constructor
headers_as_set = set(chain.from_iterable(lis))

# you asked for a list
headers = list(
    set(chain.from_iterable(lis))
)

4
>>> lis=[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5, "height":4},
{"name": "Pam", "age": 7, "weight":90}
]
>>> {z for y in (x.keys() for x in lis) for z in y}
set(['age', 'name', 'weight', 'height'])

3

从@AshwiniChaudhary的答案中借鉴了lis,这里解释一下如何解决你的问题。

>>> lis=[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5, "height":4},
{"name": "Pam", "age": 7, "weight":90}
]

直接迭代字典会返回它的键,因此您无需调用 keys() 来获取它们,这样可以节省每个元素中一个函数调用和一个列表构造。

>>> {k for d in lis for k in d}
set(['age', 'name', 'weight', 'height'])

或者使用 itertools.chain

>>> from itertools import chain
>>> {k for k in chain(*lis)}
set(['age', 'name', 'weight', 'height'])

2
以下示例将提取键:
set_ = set()
for dict_ in dictionaries:
    set_.update(dict_.keys())
print set_

0
如果顺序对您很重要,请继续阅读...
输入您的数据:
>>> list_of_dicts = [{'age': 10, 'name': 'Tom'},{'age': 5, 'name': 'Mark', 'height': 4}, {'age': 7, 'name': 'Pam', 'weight': 90}]

定义你的函数:

>>> def get_all_keys_in_order(list_of_dicts):
        ordered_keys = []
        for dict_ in list_of_dicts:
            for key in dict_:
                if key not in ordered_keys:
                    ordered_keys.append(key)
        return ordered_keys

运行您的函数以获取输出:

>>> get_all_keys_in_order(list_of_dicts)
['age', 'name', 'height', 'weight']

@Momchill 我认为这解决了你的问题。请注意,这个算法比集合解决方案慢,如果你处理大数据可能会有问题。但对于小数据没有问题。 - mareoraft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接