反转字典映射

1030

假设有如下字典:

my_map = {'a': 1, 'b': 2}

如何反转该地图以获得以下结果:
inv_map = {1: 'a', 2: 'b'}
33个回答

11

词典中值为集合的情况,例如:

some_dict = {"1":{"a","b","c"},
        "2":{"d","e","f"},
        "3":{"g","h","i"}}
逆变表示为:
some_dict = {vi: k  for k, v in some_dict.items() for vi in v}
输出结果如下:
{'c': '1',
 'b': '1',
 'a': '1',
 'f': '2',
 'd': '2',
 'e': '2',
 'g': '3',
 'h': '3',
 'i': '3'}

如果字典的值是一个列表,这个方法同样适用。谢谢。 - catris25

10

列表和字典推导式的结合。可以处理重复的键。

{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}

4
像https://dev59.com/iHRB5IYBdhLWcg3w3K8J#41861007一样,这是一个O(n²)的解决方案,而这个问题可以很容易地使用几行额外的代码以O(n)的复杂度解决。 - Mark Amery

3

我发现这个版本比一个有10000个键的字典的已接受版本要快10%以上。

d = {i: str(i) for i in range(10000)}

new_d = dict(zip(d.values(), d.keys()))

2

我知道这个问题已经有很多好的答案了,但我想分享一个非常简洁的解决方案,同时也处理了重复值:

def dict_reverser(d):
    seen = set()
    return {v: k for k, v in d.items() if v not in seen or seen.add(v)}

这取决于 Python 中 set.add 永远返回 None 的事实。

2
除了上面提到的其他功能,如果你喜欢lambda表达式:
invert = lambda mydict: {v:k for k, v in mydict.items()}

或者,你也可以这样做:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )

2
-1;你所做的只是将页面上其他答案放入lambda中。此外,将lambda分配给变量是PEP 8的违规行为。 - Mark Amery

2

如果值不唯一,而你有点儿极端:

inv_map = dict(
    (v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())]) 
    for v in set(my_map.values())
)

特别是对于大型字典,注意这个解决方案比答案 Python反转/反向映射 要低效得多,因为它多次循环 items()


9
这段话的意思是这段代码难以阅读,不易于维护,不是一个好的编码实例。我不会给出负分,因为它仍然回答了问题,这只是我的个人意见。 - Russ Bradberry

2

我认为最好的方法是定义一个类。这里是一个“对称字典”的实现:

class SymDict:
    def __init__(self):
        self.aToB = {}
        self.bToA = {}

    def assocAB(self, a, b):
        # Stores and returns a tuple (a,b) of overwritten bindings
        currB = None
        if a in self.aToB: currB = self.bToA[a]
        currA = None
        if b in self.bToA: currA = self.aToB[b]

        self.aToB[a] = b
        self.bToA[b] = a
        return (currA, currB)

    def lookupA(self, a):
        if a in self.aToB:
            return self.aToB[a]
        return None

    def lookupB(self, b):
        if b in self.bToA:
            return self.bToA[b]
        return None

如果需要,删除和迭代方法很容易实现。

这种实现比反转整个字典(似乎是此页面上最流行的解决方案)要更有效。更不用说,您可以随意添加或删除SymDict中的值,您的反向字典始终有效--如果您仅仅反转整个字典一次,则不是这种情况。


我喜欢这个想法,尽管需要额外的内存来实现改进的计算,但值得注意的是这种方式会进行权衡。更好的方法可能是缓存或延迟计算镜像。值得注意的是,可以通过例如字典视图和自定义运算符使其在语法上更具吸引力。 - Brian M. Hunt
@BrianM.Hunt 它会牺牲一些内存,但不是很多。你只需要为每个对象存储两组指针。如果你的对象比单个整数大得多,这不会有太大影响。但如果你有一个巨大的小对象表,那么你可能需要考虑这些建议... - NcAdams
我同意,这里还有更多工作要做——我可能会稍后将其扩展为完全功能的数据类型。 - NcAdams
2
“这种实现方式比整个字典反转要高效得多” - 呃,为什么?我看不出来这种方法有什么显著的性能优势;你仍然有两个字典。如果说有什么区别的话,我会认为这比使用推导式反转字典要,因为如果你反转字典,Python 可以合理地预先知道在底层 C 数据结构中分配多少个桶,并创建反向映射,而不必调用 dictresize,但这种方法却剥夺了 Python 这种可能性。 - Mark Amery

2
这个处理非唯一值,并保留了许多唯一情况下的样式。
inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}

对于Python 3.x,请将itervalues替换为values

4
这种方法很优雅且可以处理非唯一值的情况。但是它的时间复杂度为O(n2),这意味着如果您的初始字典中有几十个元素,那么使用它应该是可以接受的,但是如果有数十万个元素,则速度将太慢以至于无法实际使用。基于defaultdict的解决方案比这种方法要快得多。 - gabuzo
Gabuzo说得很对。这个版本(可以说)比一些版本更清晰,但不适用于大数据。 - Ersatz Kwisatz

1
dict([(value, key) for key, value in d.items()])

1
这是另一种做法。
my_map = {'a': 1, 'b': 2}

inv_map= {}
for key in my_map.keys() :
    val = my_map[key]
    inv_map[val] = key

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接