Дано такий словник:
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 2.7.x
inv_map = {v: k for k, v in my_map.iteritems()}
Для Python 3+:
inv_map = {v: k for k, v in my_map.items()}
The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon
. Немає гарантій, що він залишиться таким, тому не пишіть код, покладаючись на Dict
таку саму поведінку, як OrderedDict
.
Якщо припустити, що значення в диктаті унікальні:
dict((v, k) for k, v in my_map.iteritems())
iteritems()
виведе, тому можна припустити, що довільний ключ буде призначений для не унікального значення таким чином, який буде явно відтворюваним за деяких умов, але ні в цілому.
iteritems()
методу і такий підхід не буде працювати; використовувати items()
замість цього, як показано у прийнятій відповіді. Також розуміння словника зробить це красивішим, ніж дзвінок dict
.
Якщо значення my_map
не унікальні:
inv_map = {}
for k, v in my_map.iteritems():
inv_map[v] = inv_map.get(v, [])
inv_map[v].append(k)
inv_map.get(v, [])
повертає вже доданий список, якщо такий є, тому призначення не скидається до порожнього списку. setdefault
все-таки було б гарніше.
inv_map.setdefault(v, set()).add(k)
.
my_map.items()
замість my_map.iteritems()
.
Для цього, зберігаючи тип вашого відображення (якщо припустити, що це dict
або dict
підклас):
def inverse_mapping(f):
return f.__class__(map(reversed, f.items()))
Спробуйте це:
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}
Ще один, більш функціональний спосіб:
my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))
filter
і map
повинен померти і потрапити до списків, а не робити більше варіантів".
dict
з іншими типами відображення, такими як collections.OrderedDict
абоcollections.defaultdict
Це поширюється на відповідь Роберта , застосовуючи до того, коли значення в диктаті не є унікальними.
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)
set
. Тут застосовується внутрішній тип. Що робити, якщо я хочу знайти всі ключі, де значень немає 1
або 2
? Тоді я можу просто зробити d.keys() - inv_d[1] - inv_d[2]
(на Python 3)
Ми також можемо повернути словник з повторюваними ключами, використовуючи 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 = {'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 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
зарезервовано і не повинно використовуватися для змінних імен
my_map
таке
dictio()
? Ви мали на увазі dict()
?
Поєднання списку та розуміння словника. Може обробляти повторювані ключі
{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}
Якщо значення не унікальні, і ви трохи хардкор:
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
тому що вона все ще відповідає на питання, лише моя думка.
Крім інших запропонованих вище функцій, якщо вам подобаються лямбда:
invert = lambda mydict: {v:k for k, v in mydict.items()}
Або ви могли це зробити і так:
invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )
Я думаю, що найкращий спосіб зробити це - визначити клас. Ось реалізація "симетричного словника":
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.
Це обробляє не унікальні цінності та зберігає значну частину вигляду унікальної справи.
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
.
Функція симетрична для значень списку типів; Кортежі прикриваються списками під час виконання зворотного вироку (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
)
}
Теоретично це повинно бути швидшим, ніж додавання до набору (або додавання до списку) по черзі, як у імперативному рішенні .
На жаль, значення мають бути сортованими, сортування потрібно груповим методом.
n
елементи оригінального дикту, ваш підхід має O(n log n)
складність у часі через необхідність сортування предметів дикту, тоді як наївно-імперативний підхід має O(n)
часову складність. Наскільки я знаю, ваш підхід може бути швидшим до тих пір, поки dict
на практиці це не абсурдно велике , але теоретично це не швидше.
Я би зробив це так у python 2.
inv_map = {my_map[x] : x for x in my_map}
dict.items
(або iteritems
в Python 2) є більш ефективною, ніж вилучення кожного значення окремо під час ітерації ключів.
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) є більш ефективною, ніж вилучення кожного значення окремо під час ітерації ключів. Крім того, ви не додали жодних пояснень у відповідь, яка дублює інші.
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
а потім перехід до потрібного класу в кінці (а не починати з класу правильного типу) мені здається, що він спричиняє цілком уникнене враження від продуктивності.
Я написав це за допомогою циклу 'for' і method '.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
Якщо значення не є унікальними AND, може бути хеш (один вимір):
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
не є ітерабельним. Я не впевнений, яку поведінку ви намагалися тут реалізувати, але те, що ви насправді реалізували, - це щось дуже багато, чого ніхто не збирається хотіти.
my_map.items()
працює