Инвертирование словаря со значениями списка

Итак, у меня есть этот индекс как dict.

index = {'Testfil2.txt': ['nisse', 'hue', 'abe', 'pind'], 'Testfil1.txt': ['hue', 'abe', 
'tosse', 'svend']}

Мне нужно инвертировать индекс, чтобы это был словарь с дубликатами значений, объединенных в один ключ с двумя исходными ключами в качестве значений, например:

inverse = {'nisse' : ['Testfil2.txt'], 'hue' : ['Testfil2.txt', 'Testfil1.txt'], 
'abe' : ['Testfil2.txt', 'Testfil1.txt'], 'pind' : ['Testfil2.txt'], 'tosse' : 
['Testfil1.txt'], 'svend' : ['Testfil1.txt']

Да, я напечатал выше написанное вручную.

В моем учебнике есть такая функция для инвертирования словарей:

def invert_dict(d): 
    inverse = dict() 
    for key in d: 
        val = d[key] 
        if val not in inverse: 
            inverse[val] = [key] 
        else: 
            inverse[val].append(key) 
return inverse

Он отлично работает для простых пар ключ: значение

НО, когда я пробую эту функцию с dict, в котором есть списки как значения, такие как мой index, я получаю это сообщение об ошибке:

invert_dict(index)

Traceback (most recent call last):
    File "<pyshell#153>", line 1, in <module>
invert_dict(index)
    File "<pyshell#150>", line 5, in invert_dict
if val not in inverse:
TypeError: unhashable type: 'list'

Я искал решение в течение часа, книга не помогла, и я подозреваю, что могу каким-то образом использовать кортежи, но я не уверен, как это сделать. Любая помощь?


person Vestergaardish    schedule 18.02.2016    source источник


Ответы (3)


Я пробовал, и вы хотите использовать val not in inverse, но его нельзя проверить, если «список находится в словаре». (val — это список)

Для вашего кода простое изменение сделает то, что вы хотите:

def invert_dict(d): 
    inverse = dict() 
    for key in d: 
        # Go through the list that is saved in the dict:
        for item in d[key]:
            # Check if in the inverted dict the key exists
            if item not in inverse: 
                # If not create a new list
                inverse[item] = [key] 
            else: 
                inverse[item].append(key) 
    return inverse
person MSeifert    schedule 18.02.2016

Мое решение для реверсирования словаря. Однако он создает новый словарь new_dic:

new_dic = {}
for k,v in index.items():
    for x in v:
        new_dic.setdefault(x,[]).append(k)

Выход :

{'tosse': ['Testfil1.txt'], 'nisse': ['Testfil2.txt'], 'svend': ['Testfil1.txt'], 'abe': ['Testfil1.txt', 'Testfil2.txt'], 'pind': ['Testfil2.txt'], 'hue': ['Testfil1.txt', 'Testfil2.txt']}
person ᴀʀᴍᴀɴ    schedule 18.02.2016
comment
К вашему сведению, вся эта ерунда с try/except может быть значительно сокращена либо путем преобразования new_dic в collections.defaultdict(list), либо с помощью простого dict, заменив все try/except только на new_dic.setdefault(x, []).append(k), избегая необходимости отдельно обрабатывать наличие ключа и отсутствие ключа. - person ShadowRanger; 18.02.2016

Вы не можете использовать объекты list в качестве ключей словаря, поскольку они должны быть хешируемыми объектами. Вы можете перебрать свои элементы и использовать метод dict.setdefault для создания ожидаемого результата:

>>> new = {}
>>> 
>>> for k,value in index.items():
...     for v in value:
...         new.setdefault(v,[]).append(k)
... 
>>> new
{'hue': ['Testfil2.txt', 'Testfil1.txt'], 'svend': ['Testfil1.txt'], 'abe': ['Testfil2.txt', 'Testfil1.txt'], 'tosse': ['Testfil1.txt'], 'pind': ['Testfil2.txt'], 'nisse': ['Testfil2.txt']}

и если вы имеете дело с большими наборами данных для отказа от вызова, создающего пустой список при каждом вызове метода setdefault(), вы можете использовать collections.defaultdict(), который будет вызывать отсутствующую функцию только тогда, когда она встречает новый ключ.

from collections import defaultdict

new = defaultdict(list)
for k,value in index.items():
    for v in value:
        new[v].append(k)

>>> new
defaultdict(<type 'list'>, {'hue': ['Testfil2.txt', 'Testfil1.txt'], 'svend': ['Testfil1.txt'], 'abe': ['Testfil2.txt', 'Testfil1.txt'], 'tosse': ['Testfil1.txt'], 'pind': ['Testfil2.txt'], 'nisse': ['Testfil2.txt']})
person kasravnd    schedule 18.02.2016
comment
Это не сохраняет list всех ключей, связанных с данным значением, он удаляет все, кроме последнего увиденного ключа. - person ShadowRanger; 18.02.2016
comment
Да, столько я собрал! Это неплохо, мне просто нужно, чтобы ключи имели несколько значений, например. ''hue' имеет значения 'Testfil1.txt' и 'Testfil2.txt', и тогда все в порядке. - person Vestergaardish; 18.02.2016
comment
@ShadowRanger Да, только что исправлено. - person kasravnd; 18.02.2016
comment
@Vestergaardish На первый взгляд я просто пропустил формат списка. Пожалуйста, проверьте редактирование. - person kasravnd; 18.02.2016