Учитывая такой словарь:
my_map = {'a': 1, 'b': 2}
Как можно перевернуть эту карту, чтобы получить:
inv_map = {1: 'a', 2: 'b'}
Учитывая такой словарь:
my_map = {'a': 1, 'b': 2}
Как можно перевернуть эту карту, чтобы получить:
inv_map = {1: 'a', 2: 'b'}
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()}
my_map.items()
также работает
- person valentin; 28.02.2017
The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon
. Нет никакой гарантии, что так и будет, поэтому не пишите код, полагаясь на то, что Dict
будет вести себя так же, как OrderedDict
.
- person Mattias; 26.08.2018
Предполагая, что значения в dict уникальны:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
, поэтому можно предположить, что произвольный ключ будет назначен для неуникального значения таким образом, который, по-видимому, будет воспроизводиться при некоторых условиях, но не в целом.
- person Evgeni Sergeev; 21.04.2015
iteritems()
, и этот подход не будет работать; вместо этого используйте items()
, как показано в принятом ответе. Кроме того, понимание словаря сделает это лучше, чем вызов dict
.
- person Mark Amery; 16.07.2016
Если значения в my_map
не уникальны:
inv_map = {}
for k, v in my_map.iteritems():
inv_map[v] = inv_map.get(v, []) + [k]
inv_map.get(v, [])
возвращает уже добавленный список, если он есть, поэтому присвоение не сбрасывается до пустого списка. Но setdefault
все равно будет красивее.
- person Mark Amery; 16.07.2016
inv_map.setdefault(v, set()).add(k)
.
- person Artyer; 11.08.2017
my_map.items()
вместо my_map.iteritems()
.
- person apitsch; 15.04.2019
setdefault
, используя defaultdict
: inv_map = collections.defaultdict(set)
, а затем просто inv_map[v].add(k)
- person Conchylicultor; 30.01.2020
[]
и создание пустого списка на каждой итерации?
- person Juan C. Roldán; 18.03.2021
Чтобы сделать это с сохранением типа вашего сопоставления (при условии, что это подкласс dict
или dict
):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
f.__class__
, потому что вы уже сделали предположение, что это словарь. Я бы так сделал: dict(map(reversed, f.items()))
- person bkbilly; 05.07.2020
items
метод
- person Mr_and_Mrs_D; 27.04.2021
Попробуй это:
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)
или используя понимание dict python 3.0
inv_map = {my_map[k] : k for k in my_map}
Другой, более функциональный способ:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
и map
должны умереть и быть включены в состав списков, а не увеличиваться в количестве вариантов.
- person Brian M. Hunt; 26.02.2014
dict
другими типами сопоставления, такими как collections.OrderedDict
или collections.defaultdict
- person Will S; 17.08.2017
Это расширяет ответ Роберта, применимый к случаям, когда значения в dict не уникальны.
class ReversibleDict(dict):
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.iteritems():
revdict.setdefault(v, []).append(k)
return revdict
Реализация ограничена тем, что вы не можете использовать reversed
дважды и получить обратно оригинал. Это не симметрично как таковое. Он протестирован с Python 2.6. Здесь показан пример того, как я использую для печати результирующего словаря.
Если вы предпочитаете использовать set
, чем list
, и могут существовать неупорядоченные приложения, для которых это имеет смысл, вместо setdefault(v, []).append(k)
используйте setdefault(v, set()).add(k)
.
revdict.setdefault(v, set()).add(k)
- person mueslo; 22.12.2016
set
. Здесь применяется внутренний тип. Что, если я хочу найти все ключи, значения которых не равны 1
или 2
? Тогда я могу просто сделать d.keys() - inv_d[1] - inv_d[2]
(в Python 3)
- person mueslo; 22.12.2016
Мы также можем перевернуть словарь с повторяющимися ключами, используя 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()
.
dict(d_inv)
, поскольку словарь имеет более широкую поддержку, чем менее стандартный defaultdict
. Например, некоторые сериализаторы (такие как yaml.safe_dump
) не будут сериализовать defaultdict
, в то время как они будут сериализовать dict
.
- person Konstantin; 22.12.2020
Комбинация понимания списка и словаря. Может обрабатывать повторяющиеся ключи
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Например, у вас есть следующий словарь:
dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}
И вы хотите получить это в таком перевернутом виде:
inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}
Первое решение. Для инвертирования пар ключ-значение в словаре используйте подход for
-цикла:
# Use this code to invert dictionaries that have non-unique values
inverted_dict = dict()
for key, value in 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 dict.items()}
Третье решение. Используйте подход возврата к инверсии (полагается на второе решение):
# Use this code to invert dictionaries that have lists of values
dict = {value: key for key in inverted_dict for value in my_map[key]}
dict
зарезервирован и не должен использоваться для имен переменных
- person crypdick; 19.08.2019
my_map
- person crypdick; 19.08.2019
dictio()
? Вы имели в виду dict()
?
- person Georgy; 01.10.2019
Я думаю, что лучший способ сделать это - определить класс. Вот реализация «симметричного словаря»:
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 столько, сколько захотите, и ваш обратный словарь всегда будет оставаться действительным - это неверно, если вы просто перевернете весь словарь один раз.
dictresize
, но этот подход лишает Python такой возможности.
- person Mark Amery; 16.07.2016
Если значения не уникальны, а вы немного хардкорны:
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()
несколько раз.
-1
, потому что он все еще отвечает на вопрос, просто мое мнение.
- person Russ Bradberry; 03.10.2012
Это обрабатывает неуникальные значения и сохраняет большую часть внешнего вида уникального корпуса.
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
.
Я обнаружил, что эта версия более чем на 10% быстрее принятой версии словаря с 10000 ключами.
d = {i: str(i) for i in range(10000)}
new_d = dict(zip(d.values(), d.keys()))
Случай, когда значения словаря являются набором. Нравиться:
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'}
В дополнение к другим функциям, предложенным выше, если вам нравятся лямбды:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Или вы тоже можете сделать это так:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Вот еще один способ сделать это.
my_map = {'a': 1, 'b': 2}
inv_map= {}
for key in my_map.keys() :
val = my_map[key]
inv_map[val] = key
Я знаю, что на этот вопрос уже есть много хороших ответов, но я хотел поделиться этим очень изящным решением, которое также заботится о повторяющихся значениях:
def dict_reverser(d):
seen = set()
return {v: k for k, v in d.items() if v not in seen or seen.add(v)}
Это основано на том факте, что set.add
всегда возвращает None
в Python.
Много ответов, но не нашел ничего чистого, если мы говорим о словаре с неуникальными значениями.
Решение было бы:
from collections import defaultdict
inv_map = defaultdict(list)
for k, v in my_map.items():
inv_map[v].append(k)
Если исходный dict my_map = {'c': 1, 'd': 5, 'a': 5, 'b': 10}
тогда выполнение приведенного выше кода даст:
{5: ['a', 'd'], 1: ['c'], 10: ['b']}
Функция симметрична для значений типа list; Кортежи превращаются в списки при выполнении reverse_dict (reverse_dict (словарь))
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
Поскольку словарям требуется один уникальный ключ в словаре, в отличие от значений, мы должны добавить перевернутые значения в список сортировки, который будет включен в новые конкретные ключи.
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
Быстрое функциональное решение для небиективных карт (значения не уникальны):
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.
n
элементов в исходном dict, ваш подход имеет O(n log n)
временную сложность из-за необходимости сортировать элементы dict, тогда как наивный императивный подход имеет O(n)
временную сложность. Насколько мне известно, ваш подход может быть быстрее до абсурдно больших dict
s в практике, но, конечно, это не быстрее в теории.
- person Mark Amery; 16.07.2016
Попробуйте это для python 2.7 / 3.x
inv_map={};
for i in my_map:
inv_map[my_map[i]]=i
print inv_map
Лямбда-решение для текущих версий Python 3.x:
d1 = dict(alice='apples', bob='bananas')
d2 = dict(map(lambda key: (d1[key], key), d1.keys()))
print(d2)
Результат:
{'apples': 'alice', 'bananas': 'bob'}
Это решение не проверяет дубликаты.
Некоторые замечания:
for
цикла. Он также избегает использования list comprehension
для тех, кто плохо разбирается в математике ;-)Я бы сделал это в python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(или iteritems
в Python 2) более эффективна, чем извлечение каждого значения отдельно при итерации ключей.
- person jpp; 23.08.2019
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']}
dict.items
(или iteritems
в Python 2) более эффективна, чем извлечение каждого значения отдельно при итерации ключей. Кроме того, вы не добавили никаких объяснений к ответу, который дублирует другие.
- person jpp; 25.08.2019
def reverse_dictionary(input_dict):
out = {}
for v in input_dict.values():
for value in v:
if value not in out:
out[value.lower()] = []
for i in input_dict:
for j in out:
if j in map (lambda x : x.lower(),input_dict[i]):
out[j].append(i.lower())
out[j].sort()
return out
этот код делает так:
r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})
print(r)
{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}
Не совсем другое, просто немного переписанный рецепт из Cookbook. Более того, он оптимизирован за счет сохранения setdefault
метода вместо того, чтобы каждый раз получать его через экземпляр:
def inverse(mapping):
'''
A function to inverse mapping, collecting keys with simillar values
in list. Careful to retain original type and to be fast.
>> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
>> inverse(d)
{1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
'''
res = {}
setdef = res.setdefault
for key, value in mapping.items():
setdef(value, []).append(key)
return res if mapping.__class__==dict else mapping.__class__(res)
Предназначен для работы под CPython 3.x, для 2.x замените mapping.items()
на mapping.iteritems()
На моей машине работает немного быстрее, чем другие примеры здесь
dict
и последующее преобразование в желаемый класс в конце (вместо того, чтобы начинать с класса правильного типа), на мой взгляд, влечет за собой снижение производительности, которого можно полностью избежать.
- person Mark Amery; 15.07.2019
Я написал это с помощью цикла for и метода .get () и изменил имя map словаря на map1, потому что map - это функция.
def dict_invert(map1):
inv_map = {} # new dictionary
for key in map1.keys():
inv_map[map1.get(key)] = key
return inv_map
Если значения не уникальны, И может быть хеш (одно измерение):
for k, v in myDict.items():
if len(v) > 1:
for item in v:
invDict[item] = invDict.get(item, [])
invDict[item].append(k)
else:
invDict[v] = invDict.get(v, [])
invDict[v].append(k)
И с рекурсией, если вам нужно копнуть глубже, то только одно измерение:
def digList(lst):
temp = []
for item in lst:
if type(item) is list:
temp.append(digList(item))
else:
temp.append(item)
return set(temp)
for k, v in myDict.items():
if type(v) is list:
items = digList(v)
for item in items:
invDict[item] = invDict.get(item, [])
invDict[item].append(k)
else:
invDict[v] = invDict.get(v, [])
invDict[v].append(k)
{"foo": "bar"}
в {'b': ['foo'], 'a': ['foo'], 'r': ['foo']}
и вызывает исключение, если какое-либо значение в myDict
не является повторяемым. Я не уверен, какое поведение вы пытались реализовать здесь, но то, что вы на самом деле реализовали, практически никому не понадобится.
- person Mark Amery; 15.07.2019