Элегантно разбивая список (или dict) на два через какую-то произвольную функцию в python
Есть ли элегантный способ разделения списка /dict на два списка /dicts в python, взяв какую-то произвольную функцию сплиттера?
Я мог бы легко иметь два представления списка или два выбора, но мне кажется, что должен быть какой-то лучший способ сделать это, что позволяет избежать повторения каждого элемента дважды.
Я мог бы сделать это легко с помощью цикла for и if, но это занимает примерно 7 строк кода, что должно быть очень простой операцией.
Любые идеи?
Edit:
Только для справки, мои два решения будут,
# given dict cows, mapping cow names to weight
# fast solution
fatcows = {}
thincows = {}
for name, weight in cows:
if weight < 100:
thincows[name] = weight
else:
fatcows[name] = weight
# double-list-comprehension solution would be
fatcows = {name: weight for name, weight in cows.items() if weight > 100}
thincows = {name: weight for name, weight in cows.items() if weight < 100}
Я думал, что должно быть что-то более элегантное, чем это, о котором я никогда не думал, что-то вроде:
thincows, fatcows = ??? short expression involving cows ???
Я знаю, что это можно сделать, написав функции функций более высокого порядка, чтобы сделать это для меня, и я знаю, как это сделать вручную. Мне просто интересно, есть ли какая-то супер-элегантная языковая функция, чтобы сделать это для меня.
Это похоже на то, что вы можете написать свои собственные подпрограммы и еще что делать SELECT в списке или просто сказать
thincows = select(cows, lambda c: c.weight < 100)
Я надеялся, что будет такой же элегантный способ разделения списка с одним проходом
Ответы
Ответ 1
Больше удовольствия от коров:)
import random; random.seed(42)
cows = {n:random.randrange(50,150) for n in 'abcdefghijkl'}
thin = {}
for name, weight in cows.iteritems():
thin.setdefault(weight < 100, {})[name] = weight
>>> thin[True]
{'c': 77, 'b': 52, 'd': 72, 'i': 92, 'h': 58, 'k': 71, 'j': 52}
>>> thin[False]
{'a': 113, 'e': 123, 'l': 100, 'g': 139, 'f': 117}
Ответ 2
Как насчет трех строк?
fatcows, thincows = {}, {}
for name, weight in cows.items():
(fatcows if weight > 50 else thincows)[name] = weight
Испытано:
>>> cows = {'bessie':53, 'maud':22, 'annabel': 77, 'myrna':43 }
>>> fatcows, thincows = {}, {}
>>> for name, weight in cows.items():
... (fatcows if weight > 50 else thincows)[name] = weight
...
>>> fatcows
{'annabel': 77, 'bessie': 53}
>>> thincows
{'maud': 22, 'myrna': 43}
Ответ 3
Любое решение будет принимать O (N) время для вычисления, будь то через два прохода через список или один проход, который больше работает на элемент. Самый простой способ - использовать только те инструменты, которые вам доступны: itertools.ifilter
и itertools.ifilterfalse
:
def bifurcate(predicate, iterable):
"""Returns a tuple of two lists, the first of which contains all of the
elements x of `iterable' for which predicate(x) is True, and the second
of which contains all of the elements x of `iterable` for which
predicate(x) is False."""
return (itertools.ifilter(predicate, iterable),
itertools.ifilterfalse(predicate, iterable))
Ответ 4
Это можно сделать с помощью genex, sorting и itertools.groupby()
, но это, вероятно, будет не намного более эффективным, чем решение грубой силы.
Решение грубой силы:
def bifurcate(pred, seq):
if pred is None:
pred = lambda x: x
res1 = []
res2 = []
for i in seq:
if pred(i):
res1.append(i)
else:
res2.append(i)
return (res2, res1)
Элегантное решение:
import itertools
import operator
def bifurcate(pred, seq):
if pred is None:
pred = lambda x: x
return tuple([z[1] for z in y[1]] for y in
itertools.groupby(sorted((bool(pred(x)), x) for x in seq),
operator.itemgetter(0)))
Ответ 5
Довольно простой, без каких-либо внешних инструментов:
my_list = [1,2,3,4]
list_a = []
list_b = []
def my_function(num):
return num % 2
generator = (list_a.append(item) if my_function(item) else list_b.append(item)\
for item in my_list)
for _ in generator:
pass
Ответ 6
ОК, его о коровах:)
cows = {'a': 123, 'b': 90, 'c': 123, 'd': 70}
select = lambda cows, accept: {name: weight for name, weight
in cows.items()
if accept(weight)}
thin = select(cows, lambda x: x < 100)
fat = select(cows, lambda x: x > 100)