2016-02-14 2 views
2

Я хотел бы переформатировать следующий список, содержащие кортежи с целыми числами (разделяемых между некоторыми кортежами) и строки (своеобразные для каждого кортежа)Python 2.7: эффективно переформатировать список кортежей

mylist = [(8, 'dddd'), (8, '33333'), (8, 'fdsss'), (9, 'fsfjs'),(10, 'dddd'), (10, '33333'), (12, 'fdsss'), (12, 'fsfjs')] 

так, что каждый кортеж содержит целое и каскадная строка всех строк, принадлежащих к нему, например, так:

mynewlist = [(8, 'dddd, 33333, fdsss'), (9, 'fsfjs'),(10, 'dddd, 333333'), (12, 'fdsss, fsfjs') 

После некоторых раздумий, самые экономное решение, которое я придумал, это просто цикл по всем кортежам и конкатенация строк, пока число не Безразлично 't соответствует следующей:

mynewlist = [] 
label = '' 
for i in range(len(mylist)-1): 
    if mylist[i][0] != mylist[i+1][0]: 
     mynewlist.append(tuple([mylist[i][0], label + mylist[i][1]])) 
     label = '' 
    else: 
     label = label + mylist[i][1] + ',' 

Это прекрасно работает. Тем не менее, я хотел бы знать, есть ли более эффективный/Pythonic способ создания списка. Я рассмотрел возможность использования списка, но это не позволило бы мне выбирать строки, не переходя через весь список много раз; то для каждого уникального целого числа должно быть задано понимание списка, что кажется расточительным. Я также рассмотрел предварительный выбор строк, связанных с уникальным целым числом, путем индексирования, но для меня это выглядит совершенно не-Pythonic.

Совет очень ценен. Благодаря!

ответ

6

Вы можете использовать itertools.groupby() сделать группировку здесь:

from itertools import groupby 
from operator import itemgetter 

mynewlist = [ 
    (key, ', '.join([s for num, s in group])) 
    for key, group in groupby(mylist, itemgetter(0))] 

Это использует list comprehensions для обработки каждой группы и извлечения строк из сгруппированных кортежей для конкатенации. operator.itemgetter() object говорит groupby() группе вход на первом элементе:

>>> from itertools import groupby 
>>> from operator import itemgetter 
>>> mylist = [(8, 'dddd'), (8, '33333'), (8, 'fdsss'), (9, 'fsfjs'),(10, 'dddd'), (10, '33333'), (12, 'fdsss'), (12, 'fsfjs')] 
>>> [(key, ', '.join([s for num, s in group])) for key, group in groupby(mylist, itemgetter(0))] 
[(8, 'dddd, 33333, fdsss'), (9, 'fsfjs'), (10, 'dddd, 33333'), (12, 'fdsss, fsfjs')] 

Обратите внимание, что groupby() итератора только группы последовательных согласующих элементов. Это означает, что если ваш вход не отсортирован, то кортежи с одним и тем же начальным элементом необязательно всегда собираются вместе. Если вход не сортируются и вам нужно все кортежи с тем же исходным элементом группировать независимо от того, где они находятся в последовательности ввода, использовать словарь для группы элементов первым:

grouped = {} 
for key, string in mylist: 
    grouped.setdefault(key, []).append(string) 
mynewlist = [(key, ', '.join([s for num, s in group])) for key, group in grouped.items()] 
2

defaultdict бы сделать хитрость:

from collections import defaultdict 
dct = defaultdict(list) 
for k, v in mylist: 
    dct[k].append(v) 
mynewlist = [(k, ','.join(v)) for k, v in dct.iteritems()] 
-1

Вы можете сделать это с помощью пользовательских dict подкласса:

class mydict(dict): 
    def __setitem__(self, key, val): 
     self.setdefault(key,[]).append(val) 

>>> mylist = [(8, 'dddd'), (8, '33333'), (8, 'fdsss'), 
...   (9, 'fsfjs'),(10, 'dddd'), (10, '33333'), 
...   (12, 'fdsss'), (12, 'fsfjs')] 
>>> d = mydict() 
>>> for key, val in mylist: 
...  d[key] = val 

Теперь d содержит S omething как

{8: ['dddd', '33333', 'fdsss'], 9: ['fsfjs'], 10: ['dddd', '33333'], 12: ['fdsss', 'fsfjs']} 

(в пределах порядка элементов), и вы можете легко массировать это в форме вы хотите:

result = [(key,', '.join(d[key])) for key, value in d] 
+0

новый подкласс ... Overkill. Вместо этого используйте подход 'defaultdict' или' dict.setdefault() '. Но если вы это сделаете, используйте '__missing__' вместо переопределения' __setitem__'. –

+0

Кроме того, OP хотел, чтобы строки * конкатенировались *, а не как вложенный кортеж. –

+0

@MartijnPieters '(key,) + tuple (d [ключ])' дает плоский кортеж. – BrianO

Смежные вопросы