本教程旨在詳細闡述如何在python中高效地合并和擴展包含字典的列表。通過匹配不同列表中特定鍵的值,我們可以將輔助列表中的信息(如原始名稱、原始地址)集成到主數據列表中,從而生成一個包含更豐富信息的新列表。文章將介紹利用字典進行快速查找的優化策略,并提供清晰的示例代碼和注意事項,確保數據處理的效率與準確性。
場景概述與數據結構
在數據處理的實際場景中,我們經常會遇到需要從多個數據源中整合信息的情況。假設我們有以下三組數據,它們都以字典列表的形式存儲:
-
listA: 包含名稱及其對應的原始名稱信息。
listA = [ {"name": "name sample 1", "original_name": "original name sample 1"}, {"name": "name sample 2", "original_name": "original name sample 2"}, # ... 更多數據 ]
-
listB: 包含地址及其對應的原始地址信息。
listB = [ {"address": "address sample 1", "original_address": "original address sample 1"}, {"address": "address sample 2", "original_address": "original address sample 2"}, # ... 更多數據 ]
-
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()) # 復制原始字典
這種方法雖然可以實現部分功能,但存在明顯的問題:
- 復雜性高: 如果需要合并更多輔助列表,嵌套循環的層數會增加,代碼可讀性和維護性變差。
- 效率低下: 對于每個 dataList 中的元素,都需要完整遍歷 listA 和 listB,時間復雜度為 O(len(dataList) * (len(listA) + len(listB)))。當列表數據量較大時,性能會急劇下降。
- 邏輯分散: original_name 和 original_address 的處理邏輯是分開的,容易出錯且不易擴展。
高效合并策略:利用查找字典
為了解決上述問題,我們可以采用一種更高效且更具可擴展性的方法:將輔助列表轉換為查找字典(哈希表)。字典的平均查找時間復雜度為 O(1),這能顯著提高數據匹配的效率。
核心思想
- 將 listA 轉換為一個以 name 為鍵,original_name 為值的字典。
- 將 listB 轉換為一個以 address 為鍵,original_address 為值的字典。
- 遍歷 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))
代碼解析
-
from copy import deepcopy: 導入 deepcopy 函數。這是至關重要的一步,它確保我們創建 dataList 的一個完全獨立的副本。如果直接使用 finalList = dataList,那么 finalList 和 dataList 將指向同一個內存地址,對 finalList 的修改會直接影響 dataList。deepcopy 創建了一個全新的、獨立的列表及其內部所有字典的副本。
-
創建查找字典:
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 值作為字典的值。這種預處理步驟使得后續的查找操作非常快速。
-
遍歷并合并數據:
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 的處理方式也相同。
注意事項與最佳實踐
-
性能考量: 當 listA、listB 或 dataList 的數據量非常大時,使用查找字典的方法能夠提供顯著的性能提升。其整體時間復雜度為 O(len(listA) + len(listB) + len(dataList)),遠優于多層嵌套循環的 O(N*M)。
-
數據完整性與缺失值處理:
- 在查找字典時,使用 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 鍵本身不存在也不會報錯。
-
內存使用: deepcopy 會創建一份完整的副本,這會增加內存消耗。對于極大的數據集,如果允許修改原始 dataList,可以省略 deepcopy,直接在 dataList 上進行操作,但這通常不推薦,因為它會改變原始數據。
-
可讀性與維護性: 將數據合并邏輯封裝成函數可以提高代碼的可讀性和復用性,例如:
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 和條件查找,我們可以確保數據處理的準確性,并靈活應對各種數據匹配場景。