Python обратный / инвертировать отображение
Приведенный словарь выглядит так:
my_map = { 'a': 1, 'b':2 }
Как можно инвертировать эту карту, чтобы получить:
inv_map = { 1: 'a', 2: 'b' }
ПРИМЕЧАНИЕ РЕДАКТОРА: map
изменился на my_map
чтобы избежать конфликтов со встроенной функцией, map
, Некоторые комментарии могут быть затронуты ниже.
41 ответ
Поскольку словарям требуется один уникальный ключ в словаре, в отличие от значений, мы должны добавить обратные значения в список сортировки, который будет включен в новые конкретные ключи.
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
Принятие ответа, получившего большое количество голосов, начиная с Если значения в 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)]
В зависимости от варианта использования, возможно, существует способ использовать перечисление:
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
Быстрое функциональное решение для небиективных карт (значения не уникальны):
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
)
}
Теоретически это должно быть быстрее, чем добавление к набору (или добавление в список) один за другим, как в императивном решении.
К сожалению, значения должны быть сортируемыми, сортировка требуется по группам.
Функция симметрична для значений списка типов; Кортежи включаются в списки при выполнении 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
Я написал это с помощью цикла "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)
Не что-то совершенно другое, просто немного переписанный рецепт из кулинарной книги. Это еще более оптимизируется путем сохранения 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()
На моей машине работает чуть быстрее, чем на других примерах здесь
Для всех видов словарей, независимо от того, имеют ли они уникальные значения для использования в качестве ключей, вы можете создать список ключей для каждого значения
inv_map = {v: inv_map.get(v, []) + [k] for k,v in my_map.items()}
Это не лучшее решение, но оно работает. Скажем, словарь, который мы хотим изменить:
dictionary = {'a': 1, 'b': 2, 'c': 3}, затем:
dictionary = {'a': 1, 'b': 2, 'c': 3}
reverse_dictionary = {}
for index, val in enumerate(list(dictionary.values())):
reverse_dictionary[val] = list(dictionary.keys())[index]
Выходные данные reverse_dictionary должны быть {1: 'a', 2: 'b', 3: 'c'}
Если элементы не уникальны, попробуйте это:
dict={}
dict1={}
num=int(raw_input(" how many numbers in dict?--> "))
for i in range (0,num):
key=raw_input(" enter key --> ")
value=raw_input("enter value --> ")
dict[key]=value
keys=dict.keys()
values=dict.values()
for b in range (0,num):
keys[b],values[b]=values[b],keys[b]
dict1[keys[b]]=values[b]
print keys
print values
print dict1