Разделить / разделить список на основе инварианта / хэша? - PullRequest
0 голосов
/ 28 августа 2018

У меня есть список [a1,21,...], и я хотел бы разделить его на основе значения функции f(a). Например, если вводом является список [0,1,2,3,4] и функция def f(x): return x % 3, Я хотел бы вернуть список [0,3], [1,4], [2], поскольку первая группа принимает значения 0 в f, вторая группа принимает значение 1 и т. Д.

Примерно так работает: return [[x for x in lst if f(x) == val] for val in set(map(f,lst))]

Но это не кажется оптимальным (и не пифоническим), поскольку внутренний цикл излишне сканирует весь список и вычисляет одни и те же значения f элементов несколько раз. Я ищу решение, которое бы вычисляло значение f в идеале один раз для каждого элемента ...

Ответы [ 2 ]

0 голосов
/ 29 августа 2018

@ Тим Питерс прав, а вот упомянутый setdefault и еще один itertool.groupby вариант.

С учетом

import itertools as it


iterable = range(5)
keyfunc = lambda x: x % 3

код

setdefault

d = {}
for x in iterable:
    d.setdefault(keyfunc(x), []).append(x)

list(d.values())

groupby

[list(g) for _, g in it.groupby(sorted(iterable, key=keyfunc), key=keyfunc)]

См. Также Подробнее о itertools.groupby

0 голосов
/ 28 августа 2018

Если вы не иррационально ;-) установлены на одну строку, это просто:

from collections import defaultdict

lst = [0,1,2,3,4]
f = lambda x: x % 3

d = defaultdict(list)
for x in lst:
    d[f(x)].append(x)
print(list(d.values()))

отображает то, что вы хотите. f() выполняется len(lst) раз, что не может быть побито

РЕДАКТИРОВАТЬ: или, если необходимо:

from itertools import groupby
print([[pair[1] for pair in grp]
       for ignore, grp in
       groupby(sorted((f(x), x) for x in lst),
               key=lambda pair: pair[0])])

Это не требует, чтобы f() создавало значения, используемые в качестве ключей dict, но требует дополнительных затрат на сортировку и близко к непостижимому. Ясность гораздо более питонна, чем стремление к однострочности.

...