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

2024-09-18 01:51:30 发布

您现在位置:Python中文网/ 问答频道 /正文

如何将字典中具有公共值的键合并到元组中。例如:

A = {'E2': {'5', '7'}, 'E3': {'4', '8'}, 'E5': {'5', '7'}, 'E8': {'4', '8'}}

output = {('E2', 'E5'): {'5', '7'}, ('E3', 'E8'): {'4', '8'}}

我的尝试:

^{pr2}$

打印:

OUTPUT =  {('E2', 'E5'): {'7', '5'}, ('E3', 'E8'): {'4', '8'}}

Tags: output字典元组e3e5e2pr2e8
3条回答
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 conversion是安全的,那么您可以使用一个liner代替generator:

^{pr2}$

那么,到底发生了什么?

首先,我们通过调用.items()将dict转换为元组的iterable。 我们想将这个iterable的项目组合在一起,这个iterable具有相同的第二个元素(索引为1,或者前一个dict值)。 这正是itertools.groupby所做的。这些论点是一个重要的和关键的,我们将据此进行分组。看起来,key=lambda kv: kv[1]是一条路。不幸的是没有。我们可以比较集合的相等性,但是文档说iterable应该被排序。和sorted函数需要键comparable for order。无法比较集合,因为“排序依据”列表可以。我们可以安全地创建一个包含与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并创建另一个元组的iterable。我们获取每个元组的第二个元素(iterable,索引为1)并将其转换为一个元组(这是我们未来字典的关键)。我们未来词典的价值是源于原始词典的价值。我们可以从元组的第二个元素的某个元素中获取它(这个iterable不能为空,因为groupby不能生成空组,请参见第一个片段),或者从{}将其转换回list(这是安全的,因为这个列表是从集合中生成的,所以它没有相等的元素,请参见第二个片段)。在

UPD2

在编写解释时,我发现等式键对于sorted不合适,但对于groupby来说很好,因此这里有一个更简单的解决方案,不需要定义key函数并将list转换回set:

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

输出:

^{pr2}$

我将分两次完成转换:

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

这将给出操作所期望的输出

注意,输入字典没有保证顺序,原始输入中的任何集合也没有。因此,输出不能有一个保证的术语顺序。在

相关问题 更多 >