Сгрупировать список кортежей по первому элементу кортежа
Уважаемые коллеги помогите разобраться с одной казалось бы тривиальной задачей.
Есть список кортежей
list_tuple = [('A','word1'),('A', 'word2'),('A', 'word3'),('B', 'word1'),('B','word2'),('B','word3'),('C', 'word1'),('C','word2')]
Как превратить такие данные в сгрупированный вид по первому элементу кортежа следующего вида:
group_list = [('A', ['word1', 'word2','word3']), ('B', ['word1', 'word2','word3']), ('C', ['word1', 'word2'])]
Помогите с алгоритмом. Желательно без itertools или collections
Спасибо.
Ответы (4 шт):
попробуйте так:
list_tuple = [('A','word1'),('A', 'word2'),('A', 'word3'),('B', 'word1'),('B','word2'),('B','word3'),('C', 'word1'),('C','word2')]
res = {}
for key, value in list_tuple:
if key in res:
res[key].append(value)
else:
res[key] = [value]
если надо чтобы получился список, а не словарь то в конце добавьте строку
res = list(res.items())
вариант 1 (со словарем):
list_tuple = [('A','word1'),('A', 'word2'),('A', 'word3'),('B', 'word1'),('B','word2'),('B','word3'),('C', 'word1'),('C','word2')]
tmp = dict()
for obj in list_tuple:
tmp[obj[0]] = (tmp[obj[0]] + [obj[1]]) if obj[0] in tmp else [obj[1]]
res = list(tmp.items())
print(res)
вариант 2 (без словаря, с множеством):
res = [(j, [l[1] for l in filter(lambda k: k[0] == j, list_tuple)]) for j in {i[0] for i in list_tuple}]
или
res = [(j, [l[1] for l in [k for k in list_tuple if k[0] == j]]) for j in {i[0] for i in list_tuple}]
но тут список неотсортированный по букве получается и его надо будет отсортировать тогда
Нужен dict. Без него элементы не сгруппировать. А у dict есть метод setdefault хорошо подходящий для создания отсутствующих ключей:
list_tuple = [
('A', 'word1'),
('A', 'word2'),
('A', 'word3'),
('B', 'word1'),
('B', 'word2'),
('B', 'word3'),
('C', 'word1'),
('C', 'word2')
]
d = {}
for k, v in list_tuple:
d.setdefault(k, []).append(v)
group_list = list(d.items())
print(group_list)
Результат:
[('A', ['word1', 'word2', 'word3']), ('B', ['word1', 'word2', 'word3']), ('C', ['word1', 'word2'])]
collections.namedtuple
В ряде случаев такой тип данных удобнее и эффективнее:
from collections import namedtuple
from rich import inspect
list_tuple = [
('A', 'word1'), ('A', 'word2'), ('A', 'word3'),
('B', 'word1'), ('B', 'word2'),
('C', 'word1')
]
# names = set((x[0] for x in list_tuple))
# Отсортировала имена только для красивого отображения:
names = sorted(list(set((x[0] for x in list_tuple))))
GroupVal = namedtuple(typename='GroupVal', field_names=names)
res = GroupVal._make([[v[1] for v in list_tuple if v[0] == n] for n in names])
# Пример доступа к значениям
print(f'\nЗначения получать удобнее чем в словаре\n A = {res.A}\n')
inspect(res)
output:
Значения получать удобнее чем в словаре
A = ['word1', 'word2', 'word3']
╭───── <class '__main__.GroupVal'> ──────╮
│ GroupVal(A, B, C) │
│ │
│ ╭────────────────────────────────────╮ │
│ │ GroupVal( │ │
│ │ │ A=[ │ │
│ │ │ │ 'word1', │ │
│ │ │ │ 'word2', │ │
│ │ │ │ 'word3' │ │
│ │ │ ], │ │
│ │ │ B=['word1', 'word2'], │ │
│ │ │ C=['word1'] │ │
│ │ ) │ │
│ ╰────────────────────────────────────╯ │
│ │
│ A = ['word1', 'word2', 'word3'] │
│ B = ['word1', 'word2'] │
│ C = ['word1'] │
╰────────────────────────────────────────╯