首页 > 解决方案 > 根据条件合并两个字典列表

问题描述

我有两个字典列表,我需要在任何时候合并它们USA并且GOOG它们是相同的。

list1 = 
[{'USA': 'Eastern', 
  'GOOG': '2019', 
  'Up': {'Upfront': 45}, 
  'Right': {'Upfront': 12}}, 

 {'USA': 'Western', 
  'GOOG': '2019', 
  'Up': {'Upfront': 10}, 
  'Right': {'Upfront': 15}}]

list2=
[{'USA': 'Western', 
  'GOOG': '2019', 
  'Down': {'Downback': 35}, 
  'Right': {'Downback': 25}}, 

 {'USA': 'Eastern', 
  'GOOG': '2018', 
  'Down': {'Downback': 15}, 
  'Right': {'Downback': 55}}]

因为USAGOOG中的第二个元素具有相同的值list1中的第二个元素和list2,因此它们应该被合并。预期的结果如下 -

Result = 
[{'USA': 'Eastern', 
  'GOOG': '2019', 
  'Up': {'Upfront': 45}, 
  'Right': {'Upfront': 12}}, 

 {'USA': 'Western', 
  'GOOG': '2019', 
  'Up': {'Upfront': 10}, 
  'Down': {'Downback': 35}, 
  'Right': {'Upfront': 15, 'Downback': 25}},

 {'USA': 'Eastern', 
  'GOOG': '2018', 
  'Down': {'Downback': 15}, 
  'Right': {'Downback': 55}}]

我们如何为此编写通用代码。我尝试使用defaultdict,但不知道如何连接任意数量的其余字典。

我的尝试:

from collections import defaultdict
dics = list1+list2

for dic in dics:
    for key, val in dic.items():
        dd[key].append(val)            

for dic in dics:
    for key, val in dic.items(): 
        dd[key].append(val)

标签: pythonlistdictionary

解决方案


您需要两个算法任务:找到 USA 和 GOOGL 具有相同值的记录,然后加入然后以如果两个记录中存在相同的键,则合并它们的值的方式执行此操作。

第一种天真的方法是有一个 for 循环来迭代 list1 的值,并且对于每个值,迭代 list2 的所有值 - 两个单独的循环不会削减它,你需要两个嵌套 for循环:

for element in list1:
    for other_element in list2:
        if ...:
            ...

虽然这种方法可行,并且适用于小型列表(例如,<1000 条记录),但它需要的时间和资源与列表大小的平方成正比 - 也就是说,对于接近 ~ 的列表我们所说的 1000 个项目 100 万次迭代。如果列表本身是 1.000.000 项,则计算将进行 1 * 10^12 次比较,这在当今的计算机中根本不可行。

因此,一个不错的解决方案是以比较键用作哈希的方式重新创建一个列表 - 这是通过将列表复制到字典中的键是您要比较的值,然后只在第二个列表上迭代一次。由于字典有固定的时间来查找项目,这将使比较次数与您的列表大小成正比。

您任务的第二部分是比较将一条记录复制到结果列表,并更新结果副本上的键,以便合并任何重复的键。为了避免在复制第一条记录时出现问题,我们使用 Python 更安全copy.deepcopy,这将确保子字典与原始记录中的对象不同,并且将保持隔离。

from copy import deepcopy

def merge_lists(list1, list2):
    # create dictionary from list1:
    dict1 = {(record["GOOG"], record["USA"]): record  for record in list1}

    #compare elements in list2 to those on list1:

    result = {}
    for record in list2:
        ckey = record["GOOG"], record["USA"]
        new_record = deepcopy(record)
        if ckey in dict1:
            for key, value in dict1[ckey].items():
                if key in ("GOOG", "USA"):
                    # Do not merge these keys
                    continue
                # Dict's "setdefault" finds a key/value, and if it is missing
                # creates a new one with the second parameter as value
                new_record.setdefault(key, {}).update(value)

        result[ckey] = new_record

    # Add values from list1 that were not matched in list2:
    for key, value in dict1.items():
        if key not in result:
            result[key] = deepcopy(value)

    return list(result.values())

推荐阅读