У меня есть список 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',)}