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
Другие вопросы по тегам