элегантное разбиение списка (или dict) на два с помощью некоторой произвольной функции в python - PullRequest
10 голосов
/ 31 июля 2011

Есть ли какой-нибудь элегантный способ разбить список / dict на два списка / dict в python, используя некоторую произвольную функцию splitter?

Я мог бы легко иметь два понимания списка или два выбора, но мне кажется, должен быть какой-то лучший способ сделать это, чтобы избежать повторения каждого элемента дважды.

Я мог бы сделать этолегко с помощью цикла for и оператора if, но для этого нужно что-то вроде 7 строк кода, что должно быть очень простой операцией.

Любые идеи?

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

Просто для справки, мои два решения были бы:

# 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)

Я надеялся, что найдется такой же элегантный способ разделение список, с одним проходом

Ответы [ 6 ]

6 голосов
/ 31 июля 2011

Как насчет 3 строк?

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}
5 голосов
/ 31 июля 2011

Любому решению потребуется 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))
3 голосов
/ 31 июля 2011

Больше веселья с коровами:)

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}
3 голосов
/ 31 июля 2011

Это можно сделать с помощью генекса, сортировки и 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)))
2 голосов
/ 31 июля 2011

Довольно просто, без каких-либо внешних инструментов:

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
1 голос
/ 31 июля 2011

ОК, это о коровах:)

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)
...