如何用Pythonic方式按指定键连接两个字典列表?
按指定键合并两个字典列表的Pythonic方案
假设我们有两个字典列表 l1 和 l2:
l1 = [ {"id": 0, "foo": 0}, {"id": 1, "foo": 1}, {"id": 2, "foo": 2}, ... ] l2 = [ {"id": 0, "bar": 0}, {"id": 1, "bar": 1}, {"id": 2, "bar": 2}, ... ]
我们需要按指定键(比如 "id")将两个列表合并,得到如下结果:
[ {"id": 0, "foo": 0, "bar": 0}, {"id": 1, "foo": 1, "bar": 1}, {"id": 2, "foo": 2, "bar": 2}, ... ]
现有方案的问题
你提到的两种方案各有不足:
- 双重循环列表推导式:时间复杂度为 O(NM),数据量稍大时效率极低,且如果两个字典存在非指定键的重复键,后遍历的字典会覆盖前者的键值:
[ {**d1, **d2} for d1 in l1 for d2 in l2 if d1["id"] == d2["id"] ] - 字典映射法:时间复杂度优化到了 O(N+M),但原写法略显繁琐,可读性可以进一步提升。
更优的解决方案
1. 简洁版字典映射法(推荐)
这个方法既保持了 O(N+M) 的高效性,又更简洁可读,还避免修改原列表的字典:
# 先将l1转换为id到字典副本的映射,避免修改原数据 merged_map = {d["id"]: d.copy() for d in l1} # 遍历l2,将每个字典的内容更新到对应id的映射中 for d in l2: merged_map[d["id"]].update(d) # 按l1的原始顺序输出结果 result = [merged_map[d["id"]] for d in l1]
2. 处理id不完整的场景
如果两个列表的id可能不重叠(比如有的id只在l1或l2中存在),可以用 collections.defaultdict 来统一处理:
from collections import defaultdict merged_map = defaultdict(dict) # 遍历两个列表的所有字典,按id合并 for d in l1 + l2: merged_map[d["id"]].update(d) # 转换为列表,Python3.7+ 中普通dict会保留插入顺序 result = list(merged_map.values())
3. 有序列表快速合并(特定场景)
如果两个列表已经按id排序且id完全一一对应,可以用 zip 配合字典解包,效率同样是 O(N):
result = [{**d1, **d2} for d1, d2 in zip(l1, l2)]
注意:这个方法要求两个列表的id顺序完全匹配,否则会出现错误合并的情况。
方案对比
| 方案 | 时间复杂度 | 适用场景 |
|---|---|---|
| 双重循环列表推导式 | O(NM) | 数据量极小的测试场景 |
| 字典映射法 | O(N+M) | 大多数场景,推荐使用 |
| zip快速合并 | O(N) | 列表已按id排序且一一对应 |
| defaultdict法 | O(N+M) | 存在id不重叠的场景 |
内容的提问来源于stack exchange,提问作者Ci Leong
相关产品推荐
相关产品推荐

