Список для преобразования словаря с несколькими значениями на ключ?

У меня есть список Python, который содержит пары ключей/значений:

l=[ [1, 'A'], [1, 'B'], [2, 'C'] ]

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

{ 1:('A', 'B'), 2:('C',) }

Итеративное решение тривиально:

l=[ [1, 'A'], [1, 'B'], [2, 'C'] ]
d={}
for pair in l:
    if d.has_key(pair[0]):
        d[pair[0]]=d[pair[0]]+tuple(pair[1])
    else:
        d[pair[0]]=tuple(pair[1])

print d

{1: ('A', 'B'), 2: ('C',)}

Есть ли более элегантное решение Pythonic для этой задачи?

Ответ 1

from collections import defaultdict

d1 = defaultdict(list)

for k, v in l:
    d1[k].append(v)

d = dict((k, tuple(v)) for k, v in d1.iteritems())

d содержит теперь {1: ('A', 'B'), 2: ('C',)}

d1 - это временный defaultdict со списками как значения, которые будут преобразованы в кортежи в последней строке. Таким образом, вы добавляете списки и не воссоздаете кортежи в основном цикле.

Ответ 2

Этот метод является относительно эффективным и довольно компактным:

reduce(lambda x, (k,v): x[k].append(v) or x, l, defaultdict(list))

В Python3 это становится (делая экспорт явным):

dict(functools.reduce(lambda x, d: x[d[0]].append(d[1]) or x, l, collections.defaultdict(list)))

Обратите внимание, что сокращение переместилось в functools и что lambdas больше не принимает кортежи. Эта версия все еще работает в версиях 2.6 и 2.7.

Ответ 3

Использование списков вместо кортежей в качестве значений dict:

l=[ [1, 'A'], [1, 'B'], [2, 'C'] ]
d={}
for key, val in l:
    d.setdefault(key, []).append(val)

print d

Ответ 4

Являются ли ключи уже отсортированными в списке ввода? В этом случае у вас есть функциональное решение:

import itertools

lst = [(1, 'A'), (1, 'B'), (2, 'C')]
dct = dict((key, tuple(v for (k, v) in pairs)) 
           for (key, pairs) in itertools.groupby(lst, lambda pair: pair[0]))
print dct
# {1: ('A', 'B'), 2: ('C',)}