Список групп по значениям - PullRequest
50 голосов
/ 17 апреля 2011

Допустим, у меня есть такой список:

list = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]

Как можно наиболее элегантно сгруппировать его, чтобы получить вывод этого списка в Python:

list = [["A", "C"], ["B"], ["D", "E"]]

Таким образом, значения сгруппированыпо второму значению, но порядок сохраняется ...

Ответы [ 6 ]

78 голосов
/ 17 апреля 2011
values = set(map(lambda x:x[1], list))
newlist = [[y[0] for y in list if y[1]==x] for x in values]
27 голосов
/ 17 апреля 2011
from operator import itemgetter
from itertools import groupby

lki = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
lki.sort(key=itemgetter(1))

glo = [[x for x,y in g]
       for k,g in  groupby(lki,key=itemgetter(1))]

print glo

.

EDIT

Другое решение, которое не требует импорта, более читабельно, сохраняет заказы и на 22% длиннее предыдущего:

oldlist = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]

newlist, dicpos = [],{}
for val,k in oldlist:
    if k in dicpos:
        newlist[dicpos[k]].extend(val)
    else:
        newlist.append([val])
        dicpos[k] = len(dicpos)

print newlist
20 голосов
/ 17 апреля 2011

Ответ Говарда лаконичен и элегантен, но в худшем случае это также O (n ^ 2).Для больших списков с большим количеством значений ключей группировки сначала необходимо отсортировать список, а затем использовать itertools.groupby:

>>> from itertools import groupby
>>> from operator import itemgetter
>>> seq = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
>>> seq.sort(key = itemgetter(1))
>>> groups = groupby(seq, itemgetter(1))
>>> [[item[0] for item in data] for (key, data) in groups]
[['A', 'C'], ['B'], ['D', 'E']]

Редактировать:

Я изменил это, увидев ответ Eyequem: itemgetter(1) лучше, чем lambda x: x[1].

7 голосов
/ 18 апреля 2011
>>> import collections
>>> D1 = collections.defaultdict(list)
>>> for element in L1:
...     D1[element[1]].append(element[0])
... 
>>> L2 = D1.values()
>>> print L2
[['A', 'C'], ['B'], ['D', 'E']]
>>> 
2 голосов
/ 17 апреля 2011

Я не знаю об элегантности, но это, безусловно, выполнимо:

oldlist = [["A",0], ["B",1], ["C",0], ["D",2], ["E",2]]
# change into: list = [["A", "C"], ["B"], ["D", "E"]]

order=[]
dic=dict()
for value,key in oldlist:
  try:
    dic[key].append(value)
  except KeyError:
    order.append(key)
    dic[key]=[value]
newlist=map(dic.get, order)

print newlist

Это сохраняет порядок первого вхождения каждой клавиши, а также порядок элементов для каждой клавиши.Требуется, чтобы ключ был хэшируемым, но в противном случае ему не присваивается значение.

1 голос
/ 17 апреля 2011
len = max(key for (item, key) in list)
newlist = [[] for i in range(len+1)]
for item,key in list:
  newlist[key].append(item)

Вы можете сделать это в едином понимании списка, возможно, более элегантно, но O (n ** 2):

[[item for (item,key) in list if key==i] for i in range(max(key for (item,key) in list)+1)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...