Гнездо плоский список на основе произвольного критерия

У меня есть плоский список уникальных объектов, некоторые из которых могут совместно использовать данный атрибут с другими. Я хочу создать вложенный список списков с объектами, сгруппированными по данному атрибуту. В качестве минимального примера, учитывая следующий список:

>>> flat = ["Shoes", "pants", "shirt", "tie", "jacket", "hat"]

Мне может понадобиться группировать его по длине, например:

>>> nest_by_length(flat)
[['tie', 'hat'], ['shoes', 'pants', 'shirt'], ['jacket']]

Я видел пару похожих questions и рекомендации. Однако во всех этих случаях вложенность основывается на упорядочении входного списка. В моем случае упорядочение входного списка полностью непредсказуемо, равно как и количество подписок для вывода и количество элементов в каждом списке.

Существует ли стандартная функция или идиоматический способ выполнения этого?

Ответ 1

Общей идиомой для существующего списка является использование groupby в itertools:

from itertools import groupby

flat = ["Shoes", "pants", "shirt", "tie", "jacket", "hat"]

result=[]
for k, g in groupby(sorted(flat, key=len), key=len):
    result.append(list(g))

print result   

Или, более кратко:

[list(g) for _,g in groupby(sorted(flat, key=len), key=len)]

Печать

[['tie', 'hat'], ['Shoes', 'pants', 'shirt'], ['jacket']]

Вход в groupby сгруппирован в группы на основе изменения значения вывода ключевой функции, в данном случае len. Как правило, вам необходимо предварительно упорядочить список на основе одной и той же ключевой функции, поэтому сначала вызывается функция sorted.

Если ваш исходный список еще не завершен или не сортируется на основе критериев (или вы предпочитаете другой вариант), создайте dict, который сопоставляет ваши критерии с уникальным значением ключа:

groups={}
for e in flat:
    groups.setdefault(len(e), []).append(e)

print groups    
# {5: ['Shoes', 'pants', 'shirt'], 3: ['tie', 'hat'], 6: ['jacket']}

Вы также можете использовать defaultdict вместо setdefault с произвольным значением ключа:

from collections import defaultdict
groups=defaultdict(list)
for e in flat:
    groups[len(e)].append(e)  
# groups=defaultdict(<type 'list'>, {5: ['Shoes', 'pants', 'shirt'], 3: ['tie', 'hat'], 6: ['jacket']})

В любом случае вы можете создать вложенный список из этого:

>>> [groups[k] for k in sorted(groups.keys())] 
[['tie', 'hat'], ['Shoes', 'pants', 'shirt'], ['jacket']]