需求

有如下列表,要将他们按照 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}]

解决方案

  1. 初级版
    将两个列表按照 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 循环会降低代码的效率。所以

  2. 第一版改进


    l3 = {x['id']: {'av': x['av']} for x in l1}

    for item in l2:

    l3[item['id']].update(nv=item['nv'])

  3. 第二版代码
    使用字典的pop方法将 id 取出来,因为我们只关心 id,而不需要关注字典中的其他 key
    l3 = {_.pop('id'): _ for _ in l1}

    for item in l2:

    l3[item.pop('id')].update(item)

    但是这种办法有一个缺陷:我们会对所有输入的字典进行更新,为了消除这个影响,我们从一个空字典开始,更新每一个键,当然也包括 id,之后弹出额外的键,可以使用defaultdict:
    简单介绍参考:James Tauber : Evolution of Default Dictionaries in Python
  4. 第三版代码

    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')

    如果我们要合并的内字典列表多于两个呢?用这种方法是很容易扩展的,定义一个方法:

  5. 终极版代码

    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 Overflowlist - merging Python dictionaries - Stack Overflow