[英]Pythonic way to merge keys with common values for a single dictionary
如何將字典的鍵與常用值合並到元組中。 例如:
A = {'E2': {'5', '7'}, 'E3': {'4', '8'}, 'E5': {'5', '7'}, 'E8': {'4', '8'}}
output = {('E2', 'E5'): {'5', '7'}, ('E3', 'E8'): {'4', '8'}}
我的嘗試:
A = {'E2': {'5', '7'}, 'E3': {'4', '8'}, 'E5': {'5', '7'}, 'E8': {'4', '8'}}
output = {}
seen = []
for k, v in A.items():
if v not in [s[1] for s in seen]: # not seen this value yet
print('NOT SEEN')
print(k, v)
seen.append([k,v])
output[k] = v
else: # already seen it
print('SEEN')
print(k, v)
# determine where we've seen it
where = [x for x in seen if x[1]==v]
output.pop(where[0][0])
output[(where[0][0], k)] = v
print('OUTPUT = ', output)
這打印:
OUTPUT = {('E2', 'E5'): {'7', '5'}, ('E3', 'E8'): {'4', '8'}}
我會在兩個過程中進行轉換:
>>> A = {'E2': {'5', '7'}, 'E3': {'4', '8'}, 'E5': {'5', '7'}, 'E8': {'4', '8'}}
# First pass: Create a reverse one-to-many mapping.
# The original set() value gets converted to a hashable frozenset()
# and used as a key. The original scalar string key gets accumulated
# in a list to track the multiple occurrences.
>>> reverse = {}
>>> for key, value in A.items():
reverse.setdefault(frozenset(value), []).append(key)
# Second pass: reverse the keys and values. The list of matching
# values gets converted to a hashable tuple (as specified by the OP)
# and the frozenset() gets restored back to the original set() type.
>>> {tuple(value) : set(key) for key, value in reverse.items()}
{('E2', 'E5'): {'5', '7'}, ('E3', 'E8'): {'8', '4'}}
這給出了OP期望的輸出。
注意,輸入字典沒有保證順序,也沒有原始輸入中的任何集合。 因此,輸出不能保證條款的有序排序。
import itertools
A = {'E2': {'5', '7'}, 'E3': {'4', '8'}, 'E5': {'5', '7'}, 'E8': {'4', '8'}}
def key(x):
# List supports ordering
return sorted(list(x[1]))
def gen():
for (group_key, group) in itertools.groupby(sorted(A.items(), key=key), key=key):
gl = list(group)
yield (tuple(x[0] for x in gl),
gl[0][1] # group_key is a list, but we want original set
)
print(dict(gen()))
如果您已准備好說服set-> list-> set轉換是安全的,那么您可以制作單行而不是生成器:
print(dict((tuple(g[0] for g in group), set(group_key)) for
(group_key, group) in
itertools.groupby(sorted(A.items(), key=key), key=key)))
UPD:那么,到底發生了什么?
首先,我們通過調用.items()
將dict轉換為可迭代的元組。 我們想要將具有相同第二個元素(具有索引1或前一個dict值)的迭代項組合在一起。 這正是itertools.groupby
所做的。 參數是一個可迭代的鍵,我們將通過它組合。 看起來, key=lambda kv: kv[1]
是要走的路。 不幸的是。 我們可以比較集合的相等性,但是文檔說可迭代應該是有序的。 並且sorted
功能需要密鑰與訂單相當。 無法按列表的順序比較集。 我們可以安全地創建一個包含與set相同元素的列表,但是我們應該對它進行排序(相等的集合可以生成具有不同順序的列表, {5, 7} == {7, 5}
,但是[5, 7] != [7, 5]
)。
現在,在排序和分組之后,我們有以下數據結構:
[
(key_dict_value as list, iterable of (dict_key, dict_value) that has dict_value == key_dict_value),
...
]
現在我們可以迭代這個iterable並創建另一個可迭代的元組。 我們采用每個元組的第二個元素(可迭代,索引為1)並將其轉換為元組(這是我們未來字典的關鍵)。 我們未來字典的值是原始字典中的值。 我們可以從元組的第二個元素的某個元素(這個可迭代不能為空,因為groupby
不能生成空組,查看第一個片段)或者從key_dict_value
轉換回列表(它是安全的,因為這個列表是從集合中生成,所以它沒有相同的元素,請參閱第二個片段)。
UPD2
當我寫作解釋時,我發現平等的關鍵不適合sorted
但是對於groupby
很好,所以這里甚至更簡單的解決方案,沒有定義key
功能並將列表轉換回設置:
print(dict((tuple(g[0] for g in group), group_key) for
(group_key, group) in itertools.groupby(sorted(A.items(),
key=lambda x: sorted(list(x[1]))),
key=lambda x: x[1])))
你可以試試這個:
from collections import defaultdict
A = {'E2': {'5', '7'}, 'E3': {'4', '8'}, 'E5': {'5', '7'}, 'E8': {'4', '8'}}
second_new = defaultdict(list)
for a, b in A.items():
second_new[tuple(b)].append(a)
final_dict = {tuple(b):set(a) for a, b in second_new.items()}
輸出:
{('E8', 'E3'): {'8', '4'}, ('E5', 'E2'): {'5', '7'}}
這是我使用理解的方法。 只需要兩個中間步驟,並且只使用內置數據類型。
# get unique values from original dict
targ_values = set([tuple(v) for v in A.values()])
# build lists of original keys that match the temp_keys
targ_values = {targ_value:[orig_key for orig_key, orig_value in A.items() if tuple(orig_value) == targ_value] for targ_value in targ_values}
# reverse the order of keys & values and convert types to get desired output
output = {tuple(v):set(k) for k, v in targ_values.items()}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.