Pythonic方法将键与单个字典的公共值合并

时间:2017-07-08 21:50:39

标签: python algorithm python-3.x dictionary merge

如何将字典的键与常用值合并到元组中。例如:

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

4 个答案:

答案 0 :(得分:4)

我会在两遍中进行转换:

>>> 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预期的输出。

注意,输入字典没有保证顺序,也没有原始输入中的任何集合。因此,输出不能保证条款的有序排序。

答案 1 :(得分:2)

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->设置转换是安全的,那么您可以制作单行代码而不是生成器:

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

答案 2 :(得分:2)

你可以试试这个:

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

答案 3 :(得分:1)

这是我使用理解的方法。只需要两个中间步骤,并且只使用内置数据类型。

# 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()}