Python中基于鍵值匹配合并與擴展字典列表的實用指南

Python中基于鍵值匹配合并與擴展字典列表的實用指南

本教程旨在詳細闡述如何在python中高效地合并和擴展包含字典的列表。通過匹配不同列表中特定鍵的值,我們可以將輔助列表中的信息(如原始名稱、原始地址)集成到主數據列表中,從而生成一個包含更豐富信息的新列表。文章將介紹利用字典進行快速查找的優化策略,并提供清晰的示例代碼和注意事項,確保數據處理的效率與準確性。

場景概述與數據結構

在數據處理的實際場景中,我們經常會遇到需要從多個數據源中整合信息的情況。假設我們有以下三組數據,它們都以字典列表的形式存儲:

  1. listA: 包含名稱及其對應的原始名稱信息。

    listA = [   {"name": "name sample 1", "original_name": "original name sample 1"},   {"name": "name sample 2", "original_name": "original name sample 2"},   # ... 更多數據 ]
  2. listB: 包含地址及其對應的原始地址信息。

    listB = [   {"address": "address sample 1", "original_address": "original address sample 1"},   {"address": "address sample 2", "original_address": "original address sample 2"},   # ... 更多數據 ]
  3. dataList: 我們的主數據列表,包含ID、創建時間、名稱和地址。

    立即學習Python免費學習筆記(深入)”;

    dataList = [   {"id": "1", "created_at": "date 1", "name": "name sample 1", "address": "address sample 1"},   {"id": "2", "created_at": "date 2", "name": "name sample 2", "address": "address sample 2"},   # ... 更多數據 ]

我們的目標是創建一個新的列表 finalList,它基于 dataList 的內容,并通過匹配 name 字段從 listA 中獲取 original_name,以及通過匹配 address 字段從 listB 中獲取 original_address,最終的 finalList 結構應如下所示:

finalList = [   {     "id": "1",     "created_at": "date 1",     "name": "name sample 1",     "original_name": "original name sample 1",     "address": "address sample 1",     "original_address": "original address sample 1",   },   {     "id": "2",     "created_at": "date 2",     "name": "name sample 2",     "original_name": "original name sample 2",     "address": "address sample 2",     "original_address": "original address sample 2",   },   # ... ]

傳統遍歷方法的局限性

初學者可能會嘗試使用多層嵌套循環來解決這個問題,例如:

# 示例:僅處理 original_name 的嵌套循環嘗試 finalList_partial = [] for data in dataList:     found_name = False     for item_a in listA:         if "name" in data and data["name"] == item_a["name"]:             new_entry = {                 "id": data["id"],                 "created_at": data["created_at"],                 "name": data["name"],                 "original_name": item_a["original_name"],                 "address": data["address"], # 注意:這里還沒有 original_address             }             finalList_partial.append(new_entry)             found_name = True             break # 找到匹配項后跳出內層循環     if not found_name: # 如果沒有找到匹配的name,也要添加原始數據         finalList_partial.append(data.copy()) # 復制原始字典

這種方法雖然可以實現部分功能,但存在明顯的問題:

  1. 復雜性高: 如果需要合并更多輔助列表,嵌套循環的層數會增加,代碼可讀性和維護性變差。
  2. 效率低下: 對于每個 dataList 中的元素,都需要完整遍歷 listA 和 listB,時間復雜度為 O(len(dataList) * (len(listA) + len(listB)))。當列表數據量較大時,性能會急劇下降。
  3. 邏輯分散: original_name 和 original_address 的處理邏輯是分開的,容易出錯且不易擴展。

高效合并策略:利用查找字典

為了解決上述問題,我們可以采用一種更高效且更具可擴展性的方法:將輔助列表轉換為查找字典(哈希表)。字典的平均查找時間復雜度為 O(1),這能顯著提高數據匹配的效率。

核心思想

  1. 將 listA 轉換為一個以 name 為鍵,original_name 為值的字典。
  2. 將 listB 轉換為一個以 address 為鍵,original_address 為值的字典。
  3. 遍歷 dataList 的副本,對于每個元素,使用其 name 和 address 作為鍵,在預先構建的查找字典中直接獲取 original_name 和 original_address。

示例代碼

from copy import deepcopy  # 原始數據 listA = [   {"name": "name sample 1", "original_name": "original name sample 1"},   {"name": "name sample 2", "original_name": "original name sample 2"},   {"name": "name sample 3", "original_name": "original name sample 3"}, # 增加一個未在dataList中匹配的示例 ]  listB = [   {"address": "address sample 1", "original_address": "original address sample 1"},   {"address": "address sample 2", "original_address": "original address sample 2"},   {"address": "address sample 3", "original_address": "original address sample 3"}, # 增加一個未在dataList中匹配的示例 ]  dataList = [   {"id": "1", "created_at": "date 1", "name": "name sample 1", "address": "address sample 1"},   {"id": "2", "created_at": "date 2", "name": "name sample 2", "address": "address sample 2"},   {"id": "3", "created_at": "date 3", "name": "name sample 3", "address": "address sample 4"}, # name匹配,address不匹配   {"id": "4", "created_at": "date 4", "name": "name sample 4", "address": "address sample 3"}, # address匹配,name不匹配   {"id": "5", "created_at": "date 5", "name": "name sample 5", "address": "address sample 5"}, # 均不匹配 ]  # 1. 創建查找字典以提高效率 # name_lookup: {"name sample 1": "original name sample 1", ...} name_lookup = {item["name"]: item["original_name"] for item in listA} # address_lookup: {"address sample 1": "original address sample 1", ...} address_lookup = {item["address"]: item["original_address"] for item in listB}  # 2. 深度復制dataList以避免修改原始數據 finalList = deepcopy(dataList)  # 3. 遍歷finalList并添加匹配的數據 for item in finalList:     # 嘗試根據name查找original_name     if "name" in item and item["name"] in name_lookup:         item["original_name"] = name_lookup[item["name"]]      # 嘗試根據address查找original_address     if "address" in item and item["address"] in address_lookup:         item["original_address"] = address_lookup[item["address"]]  print("原始 dataList:") import json print(json.dumps(dataList, indent=4, ensure_ascii=False))  print("n合并后的 finalList:") print(json.dumps(finalList, indent=4, ensure_ascii=False))

代碼解析

  1. from copy import deepcopy: 導入 deepcopy 函數。這是至關重要的一步,它確保我們創建 dataList 的一個完全獨立的副本。如果直接使用 finalList = dataList,那么 finalList 和 dataList 將指向同一個內存地址,對 finalList 的修改會直接影響 dataList。deepcopy 創建了一個全新的、獨立的列表及其內部所有字典的副本。

  2. 創建查找字典:

    name_lookup = {item["name"]: item["original_name"] for item in listA} address_lookup = {item["address"]: item["original_address"] for item in listB}

    這兩行代碼使用字典推導式(Dictionary Comprehension)高效地構建了兩個查找字典。例如,name_lookup 會將 listA 中每個字典的 name 值作為鍵,對應的 original_name 值作為字典的值。這種預處理步驟使得后續的查找操作非常快速。

  3. 遍歷并合并數據:

    for item in finalList:     if "name" in item and item["name"] in name_lookup:         item["original_name"] = name_lookup[item["name"]]     if "address" in item and item["address"] in address_lookup:         item["original_address"] = address_lookup[item["address"]]

    我們遍歷 finalList 中的每一個字典 item。在添加新字段之前,進行雙重檢查:

    • “name” in item:確保當前字典中存在 name 鍵(防止 KeyError)。
    • item[“name”] in name_lookup:確保 name 的值在 name_lookup 字典中存在對應的 original_name(處理沒有匹配項的情況)。 如果兩個條件都滿足,則將從 name_lookup 中查找到的 original_name 添加到當前 item 字典中。對 address 和 original_address 的處理方式也相同。

注意事項與最佳實踐

  1. 性能考量: 當 listA、listB 或 dataList 的數據量非常大時,使用查找字典的方法能夠提供顯著的性能提升。其整體時間復雜度為 O(len(listA) + len(listB) + len(dataList)),遠優于多層嵌套循環的 O(N*M)。

  2. 數據完整性與缺失值處理:

    • 在查找字典時,使用 if key in lookup_dict 或 lookup_dict.get(key) 方法是最佳實踐,可以避免因鍵不存在而引發 KeyError。
    • 如果某些 dataList 中的 name 或 address 沒有在 listA 或 listB 中找到匹配項,上述代碼將不會為其添加 original_name 或 original_address 字段。如果需要為這些未匹配項設置默認值(如 None 或空字符串),可以修改邏輯:
      item["original_name"] = name_lookup.get(item.get("name"), None) # 如果name鍵不存在或匹配不到,則為None item["original_address"] = address_lookup.get(item.get("address"), None)

      這里 item.get(“name”) 用于安全地獲取 name 鍵的值,即使 name 鍵本身不存在也不會報錯。

  3. 內存使用: deepcopy 會創建一份完整的副本,這會增加內存消耗。對于極大的數據集,如果允許修改原始 dataList,可以省略 deepcopy,直接在 dataList 上進行操作,但這通常不推薦,因為它會改變原始數據。

  4. 可讀性與維護性: 將數據合并邏輯封裝成函數可以提高代碼的可讀性和復用性,例如:

    def merge_data_lists(data_list, lookup_list_a, lookup_key_a, value_key_a, lookup_list_b, lookup_key_b, value_key_b):     name_lookup = {item[lookup_key_a]: item[value_key_a] for item in lookup_list_a}     address_lookup = {item[lookup_key_b]: item[value_key_b] for item in lookup_list_b}      merged_list = deepcopy(data_list)     for item in merged_list:         if lookup_key_a in item and item[lookup_key_a] in name_lookup:             item[value_key_a] = name_lookup[item[lookup_key_a]]         if lookup_key_b in item and item[lookup_key_b] in address_lookup:             item[value_key_b] = address_lookup[item[lookup_key_b]]     return merged_list  # 使用示例 # final_data = merge_data_lists(dataList, listA, "name", "original_name", listB, "address", "original_address")

    這種函數化處理能夠更好地適應更復雜的合并需求。

總結

在Python中處理字典列表的合并與擴展任務時,將輔助數據轉換為查找字典(哈希表)是一種高效且健壯的策略。它不僅能夠顯著提升處理大規模數據集的性能,還能使代碼邏輯更加清晰、易于維護和擴展。通過合理運用 deepcopy 和條件查找,我們可以確保數據處理的準確性,并靈活應對各種數據匹配場景。

? 版權聲明
THE END
喜歡就支持一下吧
點贊6 分享