使用单个键连接两个字典列表
- 2025-03-21 09:06:00
- admin 原创
- 46
问题描述:
给定n
以字典为元素的列表m
,我想生成一个包含一组字典的新列表。每个字典都保证有一个名为“索引”的键,但除此之外还可以有一组任意的键。非索引键永远不会在列表中重叠。例如,想象一下以下两个列表:
l1 = [{"index":1, "b":2}, {"index":2, "b":3}, {"index":3, "green":"eggs"}]
l2 = [{"index":1, "c":4}, {"index":2, "c":5}]
("b"
永远不会出现在l2
,因为它出现在l1
,同样,"c"
永远不会出现在l1
,因为它出现在l2
)
我想制作一个合并列表:
l3 = [{"index":1, "b":2, "c":4},
{"index":2, "b":3, "c":5},
{"index":3, "green":"eggs"}]
在 Python 中执行此操作的最有效方法是什么?
解决方案 1:
from collections import defaultdict
l1 = [{"index":1, "b":2}, {"index":2, "b":3}, {"index":3, "green":"eggs"}]
l2 = [{"index":1, "c":4}, {"index":2, "c":5}]
d = defaultdict(dict)
for l in (l1, l2):
for elem in l:
d[elem['index']].update(elem)
l3 = d.values()
# l3 is now:
[{'b': 2, 'c': 4, 'index': 1},
{'b': 3, 'c': 5, 'index': 2},
{'green': 'eggs', 'index': 3}]
编辑:由于l3
不能保证排序(.values()
返回项目时不按特定顺序排列),您可以按照@user560833 的建议进行操作:
from operator import itemgetter
...
l3 = sorted(d.values(), key=itemgetter("index"))
解决方案 2:
在 python 3.5 或更高版本中,您可以在单个语句中合并字典。
因此对于 python 3.5 或更高版本,一个快速的解决方案是:
from itertools import zip_longest
l3 = [{**u, **v} for u, v in zip_longest(l1, l2, fillvalue={})]
print(l3)
#[
# {'index': 1, 'b': 2, 'c': 4},
# {'index': 2, 'b': 3, 'c': 5},
# {'index': 3, 'green': 'eggs'}
#]
但是如果两个列表的大小相同,那么您可以简单地使用 zip:
l3 = [{**u, **v} for u, v in zip(l1, l2)]
注意:这假设列表按相同的方式排序index
,但OP 指出一般情况并非如此。
为了概括这种情况,一种方法是创建一个自定义的 zip-longest 类型函数,该函数仅当它们在键上匹配时才会从两个列表中产生值。
例如:
def sortedZipLongest(l1, l2, key, fillvalue={}):
l1 = iter(sorted(l1, key=lambda x: x[key]))
l2 = iter(sorted(l2, key=lambda x: x[key]))
u = next(l1, None)
v = next(l2, None)
while (u is not None) or (v is not None):
if u is None:
yield fillvalue, v
v = next(l2, None)
elif v is None:
yield u, fillvalue
u = next(l1, None)
elif u.get(key) == v.get(key):
yield u, v
u = next(l1, None)
v = next(l2, None)
elif u.get(key) < v.get(key):
yield u, fillvalue
u = next(l1, None)
else:
yield fillvalue, v
v = next(l2, None)
现在如果你有以下无序列表:
l1 = [{"index":1, "b":2}, {"index":2, "b":3}, {"index":3, "green":"eggs"},
{"index":4, "b": 4}]
l2 = [{"index":1, "c":4}, {"index":2, "c":5}, {"index":0, "green": "ham"},
{"index":4, "green": "ham"}]
使用sortedZipLongest
函数代替itertools.zip_longest
:
l3 = [{**u, **v} for u, v in sortedZipLongest(l1, l2, key="index", fillvalue={})]
print(l3)
#[{'index': 0, 'green': 'ham'},
# {'index': 1, 'b': 2, 'c': 4},
# {'index': 2, 'b': 3, 'c': 5},
# {'index': 3, 'green': 'eggs'},
# {'index': 4, 'b': 4, 'green': 'ham'}]
而原始方法会产生错误的答案:
l3 = [{**u, **v} for u, v in zip_longest(l1, l2, fillvalue={})]
print(l3)
#[{'index': 1, 'b': 2, 'c': 4},
# {'index': 2, 'b': 3, 'c': 5},
# {'index': 0, 'green': 'ham'},
# {'index': 4, 'b': 4, 'green': 'ham'}]
解决方案 3:
下面是一个实现此目的的单行代码:
[dict(sum([z.items() for z in z2],[])) for z2 in [[x3 for x3 in l1+l2 if x3['index']==key] for key in set([x1['index'] for x1 in l1]+[x2['index'] for x2 in l2])]]
不如列表推导式那么优雅。我也不认为结果一定能按你想要的方式排序。
扩展单行:
[
dict(sum([z.items() for z in z2],[]))
for z2 in [
[
x3 for x3 in l1+l2 if x3['index']==key
] for key in set(
[x1['index'] for x1 in l1]+[x2['index'] for x2 in l2]
)
]
]
第 6 行的集合表达式从两个列表中获取所有唯一索引值。围绕该列表的列表推导(第 3-9 行)创建了一个列表列表,其中每个内部列表都是该索引/键的字典组合列表,具有特定的索引值。最外层的列表推导为每个键创建一个元组对列表,并将其转换回字典列表。
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD