需求
有如下列表,要将他们按照 id 合并成一个列表。l1 = [{'id': 9, 'av': 4}, {'id': 10, 'av': 0}, {'id': 8, 'av': 0}]
l2 = [{'id': 9, 'nv': 45}, {'id': 10, 'nv': 0}, {'id': 8, 'nv': 30}]
解决方案
初级版
将两个列表按照 id 分组,分别放置到新列表中,然后遍历其中一个列表,并按照 key 将数据更新,代码如下:
l3 = {x['id']: {'av': x['av']} for x in l1}
l4 = {x['id']: {'nv': x['nv']} for x in l2}
{key: value.update(l4[key]) for key, value in l3.items()}
>> {9: {'av': 4, 'nv': 45}, 10: {'av': 0, 'nv': 0}, 8: {'av': 0, 'nv': 30}}我们很容易发现里面的 l4 的是多余的,重复 for 循环会降低代码的效率。所以
第一版改进
l3 = {x['id']: {'av': x['av']} for x in l1}
for item in l2:
l3[item['id']].update(nv=item['nv'])- 第二版代码
使用字典的pop方法将 id 取出来,因为我们只关心 id,而不需要关注字典中的其他 key但是这种办法有一个缺陷:我们会对所有输入的字典进行更新,为了消除这个影响,我们从一个空字典开始,更新每一个键,当然也包括 id,之后弹出额外的键,可以使用defaultdict:l3 = {_.pop('id'): _ for _ in l1}
for item in l2:
l3[item.pop('id')].update(item)
简单介绍参考:James Tauber : Evolution of Default Dictionaries in Python 第三版代码
from collections import defaultdict
result = defaultdict(dict)
for sequence in (l1, l2):
for dictionary in sequence:
result[dictionary['id']].update(dictionary)
for dictionary in result.values():
dictionary.pop('id')如果我们要合并的内字典列表多于两个呢?用这种方法是很容易扩展的,定义一个方法:
终极版代码
import itertools
from collections import defaultdict
def merge_iterables_of_dict(shared_key, *iterables):
result = defaultdict(dict)
for dictionary in itertools.chain.from_iterable(iterables):
result[dictionary[shared_key]].update(dictionary)
for dictionary in result.values():
dictionary.pop(shared_key)
return result使用这个函数:
merge_iterables_of_dict('id',l1,l2)
,注意,li 和 l2 的顺序会影响返回结果,更“靠谱”的数据应该放到 iterables 后方(参考字典update
方法的更新逻辑)。
参考链接
python - Merge two list of dicts with same key - Code Review Stack Exchange
推荐阅读
如果只是合并两个字典,请参阅:dictionary - How do I merge two dictionaries in a single expression in Python? - Stack Overflow和list - merging Python dictionaries - Stack Overflow