Сгрупировать список кортежей по первому элементу кортежа

Уважаемые коллеги помогите разобраться с одной казалось бы тривиальной задачей.

Есть список кортежей

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 шт):

Автор решения: Danis

попробуйте так:

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())
→ Ссылка
Автор решения: Zhihar

вариант 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}]

но тут список неотсортированный по букве получается и его надо будет отсортировать тогда

→ Ссылка
Автор решения: Stanislav Volodarskiy

Нужен 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'])]
→ Ссылка
Автор решения: AnnaBazueva

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']                          │
╰────────────────────────────────────────╯
→ Ссылка