Python: ключи поиска, которые соответствуют элементу в словаре списков

Постановка задачи

Учитывая словарь списков,

key_to_list = {
    'one': [1, 3, 5, 7],
    'two': [2, 4, 6, 8],
    'three': [1, 2, 5, 6],
    'four': [2, 5, 7, 8]
}

как лучше всего создать сопоставление элементов списков с их ключами?

list_element_to_keys = {
    1: {'one', 'three'},
    2: {'two', 'three', 'four'},
    3: {'one'},
    4: {'two'},
    5: {'one', 'three', 'four'},
    6: {'two', 'three'},
    7: {'one', 'four'},
    8: {'two', 'four'}
}

Мое решение

from collections import defaultdict

list_element_to_keys = defaultdict(set)
for key, value in key_to_list.items():
    for item in value:
        list_element_to_keys[item].add(key)

Мысли

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

Я также думаю, что это может быть какая-то itertools магия, которая могла бы помочь, но я не уверен.

Словарное понимание

С помощью друга я нашел толкование словаря, которое работает.

from itertools import chain
list_element_to_keys= { i: set(k for k,v in key_to_list.items() if i in v) for i in set(chain.from_iterable(key_to_list.values())) }

person Community    schedule 28.04.2017    source источник
comment
Это принадлежит codereview.stackexchange.com. В любом случае, я думаю, что ваше решение в порядке и его вообще не нужно менять.   -  person Alex Hall    schedule 29.04.2017


Ответы (3)


Ваше решение прекрасно, оно работает, и defaultdict — очевидный (хороший) выбор для такой проблемы.

Одна вещь, которую вы могли бы улучшить, — это использовать six.iteritems(key_to_list), что сделает его немного быстрее на Python2.

person MSeifert    schedule 28.04.2017

вы также можете сделать это

d = {}; [d.setdefault(i,[]).append(k) for k,v in key_to_list.items() for i in v]
print d

это приводит к

{1: ['three', 'one'],
 2: ['four', 'three', 'two'],
 3: ['one'],
 4: ['two'],
 5: ['four', 'three', 'one'],
 6: ['three', 'two'],
 7: ['four', 'one'],
 8: ['four', 'two']}
person plasmon360    schedule 28.04.2017
comment
Чтобы соответствовать запрошенному решению, оно должно быть d.setdefault(i,set()).add(k). Но делать понимание только ради побочных эффектов... Не знаю. - person MSeifert; 29.04.2017
comment
Спасибо, @MSeifert, я думал, что ОП хочет только сопоставление. но в любом случае, ваш комментарий приветствуется. - person plasmon360; 29.04.2017
comment
Я придумал это в какой-то момент, но, как указал MS Seifert, кажется немного неэлегантным использовать понимание только для побочных эффектов. Это определенно хороший способ сделать это, если бы нас беспокоила только игра в гольф с кодом. - person ; 02.05.2017

Я получил это во вложенном понимании с одним оператором:

  • скомпилировать набор значений (новые ключи)
  • для каждого элемента этого набора перебирать исходные ключи
  • Если исходный ключ имеет новый ключ в своем списке значений, включите эту строку в список значений нового ключа.

Код:

list_element_to_keys = \
    {new_key : [old_key for old_key in key_to_list.keys() if new_key in key_to_list[old_key]] \
     for new_key in set([item for value_list in key_to_list.values() for item in value_list ])}

print (list_element_to_keys)

Вывод (перевод строки добавлен для облегчения чтения):

{1: ['one', 'three'], 2: ['two', 'four', 'three'],
 3: ['one'], 4: ['two'],
 5: ['four', 'one', 'three'], 6: ['two', 'three'],
 7: ['four', 'one'], 8: ['two', 'four']}
person Prune    schedule 29.04.2017
comment
Это очень красиво, но кажется, что это теряет некоторую ясность. - person ; 02.05.2017
comment
Да, теряет ясность. ОП попросил понимания словаря ... :-) - person Prune; 02.05.2017