反转/倒置字典映射
- 2024-11-26 08:36:00
- admin 原创
- 189
问题描述:
给定一个这样的字典:
my_map = {'a': 1, 'b': 2}
如何反转此图以获得:
inv_map = {1: 'a', 2: 'b'}
解决方案 1:
Python 3+:
inv_map = {v: k for k, v in my_map.items()}
Python 2:
inv_map = {v: k for k, v in my_map.iteritems()}
解决方案 2:
假设字典中的值是唯一的:
Python 3:
dict((v, k) for k, v in my_map.items())
Python 2:
dict((v, k) for k, v in my_map.iteritems())
解决方案 3:
如果中的值my_map
不唯一:
Python 3:
inv_map = {}
for k, v in my_map.items():
inv_map[v] = inv_map.get(v, []) + [k]
Python 2:
inv_map = {}
for k, v in my_map.iteritems():
inv_map[v] = inv_map.get(v, []) + [k]
解决方案 4:
要在保留映射类型的同时执行此操作(假设它是一个dict
或一个dict
子类):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
解决方案 5:
尝试一下:
inv_map = dict(zip(my_map.values(), my_map.keys()))
(请注意,字典视图上的 Python 文档明确保证.keys()
它们.values()
的元素具有相同的顺序,这使得上述方法可以发挥作用。)
或者:
inv_map = dict((my_map[k], k) for k in my_map)
或者使用 python 3.0 的字典推导
inv_map = {my_map[k] : k for k in my_map}
解决方案 6:
另一种更实用的方法:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
解决方案 7:
我们还可以使用以下方法反转具有重复键的字典defaultdict
:
from collections import Counter, defaultdict
def invert_dict(d):
d_inv = defaultdict(list)
for k, v in d.items():
d_inv[v].append(k)
return d_inv
text = 'aaa bbb ccc ddd aaa bbb ccc aaa'
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}
请参阅此处:
该技术比使用 的等效技术更简单、更快捷
dict.setdefault()
。
解决方案 8:
这扩展了罗伯特的回答,适用于字典中的值不唯一的情况。
class ReversibleDict(dict):
# Ref: https://stackoverflow.com/a/13057382/
def reversed(self):
"""
Return a reversed dict, with common values in the original dict
grouped into a list in the returned dict.
Example:
>>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
>>> d.reversed()
{1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
"""
revdict = {}
for k, v in self.items():
revdict.setdefault(v, []).append(k)
return revdict
该实现的局限性在于您无法使用reversed
两次并取回原始值。它本身并不对称。它已使用 Python 2.6 进行了测试。下面是我用来打印结果字典的用例。
如果您宁愿使用set
而不是list
,并且可能存在对 有意义的无序应用程序,则setdefault(v, []).append(k)
请使用而不是setdefault(v, set()).add(k)
。
解决方案 9:
例如,您有以下词典:
my_dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
你想以这样的反转形式得到它:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
第一个解决方案。要反转字典中的键值for
对,请使用-loop 方法:
# Use this code to invert dictionaries that have non-unique values
inverted_dict = dict()
for key, value in my_dict.items():
inverted_dict.setdefault(value, list()).append(key)
第二种解决方案。使用字典理解方法进行反转:
# Use this code to invert dictionaries that have unique values
inverted_dict = {value: key for key, value in my_dict.items()}
第三种解决方案。使用反转反转方法(依赖于第二种解决方案):
# Use this code to invert dictionaries that have lists of values
my_dict = {value: key for key in inverted_dict for value in my_map[key]}
解决方案 10:
字典值是集合的情况。例如:
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'}
解决方案 11:
有很多答案,但如果我们谈论的是具有非唯一值的字典,就没有找到任何干净的东西。
解决办法是:
from collections import defaultdict
inv_map = defaultdict(list)
for k, v in my_map.items():
inv_map[v].append(k)
例子:
如果初始字典my_map = {'c': 1, 'd': 5, 'a': 5, 'b': 10}
然后运行上面的代码将得到:
{5: ['a', 'd'], 1: ['c'], 10: ['b']}
解决方案 12:
列表和字典理解的结合。可以处理重复的键
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
解决方案 13:
我发现这个版本比具有 10,000 个键的字典的接受版本快 10% 以上。
d = {i: str(i) for i in range(10000)}
new_d = dict(zip(d.values(), d.keys()))
解决方案 14:
除了上面建议的其他函数之外,如果您喜欢 lambda:
invert = lambda mydict: {v:k for k, v in mydict.items()}
或者,你也可以这样做:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
解决方案 15:
我认为最好的方法是定义一个类。下面是“对称字典”的实现:
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 中的值,并且您的逆字典将始终有效——如果您只是反转整个字典一次,情况就不一样了。
解决方案 16:
如果值不唯一,并且你有点太过严格:
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()
多次。
解决方案 17:
这可以处理非唯一值并保留大部分唯一情况的外观。
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
。
解决方案 18:
我知道这个问题已经有很多好的答案,但我想分享这个非常巧妙的解决方案,它也能处理重复值:
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
解决方案 19:
这是另一种方法。
my_map = {'a': 1, 'b': 2}
inv_map= {}
for key in my_map.keys() :
val = my_map[key]
inv_map[val] = key
解决方案 20:
dict([(value, key) for key, value in d.items()])
解决方案 21:
函数对于列表类型的值是对称的;执行 reverse_dict(reverse_dict(dictionary)) 时,元组将转换为列表
def reverse_dict(dictionary):
reverse_dict = {}
for key, value in dictionary.iteritems():
if not isinstance(value, (list, tuple)):
value = [value]
for val in value:
reverse_dict[val] = reverse_dict.get(val, [])
reverse_dict[val].append(key)
for key, value in reverse_dict.iteritems():
if len(value) == 1:
reverse_dict[key] = value[0]
return reverse_dict
解决方案 22:
由于字典要求字典中有一个唯一的键(与值不同),因此我们必须将反转的值附加到排序列表中,以包含在新的特定键中。
def r_maping(dictionary):
List_z=[]
Map= {}
for z, x in dictionary.iteritems(): #iterate through the keys and values
Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
return Map
解决方案 23:
非双射映射(值不唯一)的快速函数解:
from itertools import imap, groupby
def fst(s):
return s[0]
def snd(s):
return s[1]
def inverseDict(d):
"""
input d: a -> b
output : b -> set(a)
"""
return {
v : set(imap(fst, kv_iter))
for (v, kv_iter) in groupby(
sorted(d.iteritems(),
key=snd),
key=snd
)
}
理论上,这应该比像命令式解决方案那样逐个添加到集合(或附加到列表)更快。
不幸的是,这些值必须是可排序的,因为 groupby 需要排序。
解决方案 24:
尝试使用 Python 2.7/3.x
inv_map={};
for i in my_map:
inv_map[my_map[i]]=i
print inv_map
解决方案 25:
def invertDictionary(d):
myDict = {}
for i in d:
value = d.get(i)
myDict.setdefault(value,[]).append(i)
return myDict
print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})
这将提供以下输出:{1:['a','d'],2:['b'],3:['c']}
解决方案 26:
适用于当前 Python 3.x 版本的 lambda 解决方案:
d1 = dict(alice='apples', bob='bananas')
d2 = dict(map(lambda key: (d1[key], key), d1.keys()))
print(d2)
结果:
{'apples': 'alice', 'bananas': 'bob'}
此解决方案不检查重复项。
一些评论:
lambda 构造可以从外部作用域访问 d1,因此我们只传入当前键。它返回一个元组。
dict() 构造函数接受一个元组列表。它还接受映射的结果,因此我们可以跳过转换为列表的步骤。
此解决方案没有显式
for
循环。它还避免了list comprehension
对数学不好的人使用 ;-)
解决方案 27:
谈到得票率很高的答案如果 my_map 中的值不是唯一的:,我遇到了一个问题,即不仅值不是唯一的,而且它们是一个列表,列表中的每个项目又由三个元素的列表组成:一个字符串值、一个数字和另一个数字。
例子:
mymap['key1']
给你:
[('xyz', 1, 2),
('abc', 5, 4)]
我想只用键切换字符串值,将两个数字元素保持在同一个位置。你只需要另一个嵌套的 for 循环:
inv_map = {}
for k, v in my_map.items():
for x in v:
# with x[1:3] same as x[1], x[2]:
inv_map[x[0]] = inv_map.get(x[0], []) + [k, x[1:3]]
例子:
inv_map['abc']
现在给你:
[('key1', 1, 2),
('key1', 5, 4)]
解决方案 28:
即使原始字典中的值不唯一,这种方法仍然有效。
def dict_invert(d):
'''
d: dict
Returns an inverted dictionary
'''
# Your code here
inv_d = {}
for k, v in d.items():
if v not in inv_d.keys():
inv_d[v] = [k]
else:
inv_d[v].append(k)
inv_d[v].sort()
print(f"{inv_d[v]} are the values")
return inv_d
解决方案 29:
根据用例,可能有一种使用枚举的方法:
import enum
class Reverse(enum.Enum):
a = 1
b = 2
您可以从两个方向访问值:
Reverse.a --> prints Reverse.a
Reverse(1) --> prints Reverse.a
Reverse.a.value --> prints 1
Reverse.a.name --> prints 'a'
如果开发人员不知道“a”,而是将其包含在变量中my_var = 'a'
,则相当于my_dict[my_var]
:
getattr(Reverse, my_var) --> prints Reverse.a
解决方案 30:
我会在 python 2 中这样做。
inv_map = {my_map[x] : x for x in my_map}