séparer élégamment une liste (ou un dict) en deux via une fonction arbitraire en python

y a-t-il un moyen élégant de diviser une liste/dict en deux listes/dicts en python, en utilisant une fonction de splitter arbitraire?

je pourrais facilement avoir deux listes compréhensibles, ou deux sélections, mais il me semble qu'il devrait y avoir une meilleure façon de le faire qui évite de répéter chaque élément deux fois.

je pourrais le faire facilement avec une boucle for et if, mais qui prend quelque chose comme 7 lignes de code pour ce qui devrait être très simple opération.

des idées?

Edit:

juste pour référence, mes deux solutions seraient,

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

je pensais qu'il devait y avoir quelque chose de plus élégant que cela que je n'ai jamais pensé, quelque chose comme:

thincows, fatcows = ??? short expression involving cows ???

je sais que c'est possible de le faire en écrivant des fonctions d'ordre supérieur des choses à faire pour moi, et je sais comment le faire manuellement. Je me demandais juste s'il y avait quelque chose de très élégant dans le langage pour le faire pour moi.

c'est comme si vous pouvez écrire vos propres sous-Programmes et autres pour faire un SELECT sur une liste, ou vous pouvez juste dire

thincows = select(cows, lambda c: c.weight < 100)

j'espérais qu'il y aurait certains de la même manière élégante de fractionnement la liste, avec un pass

10
demandé sur Li Haoyi 2011-07-31 06:04:31

6 réponses

Plus de plaisir avec les vaches :)

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
répondu eryksun 2011-07-31 03:28:16

pourquoi pas 3 lignes?

fatcows, thincows = {}, {}
for name, weight in cows.items():
    (fatcows if weight > 50 else thincows)[name] = weight

testé:

>>> 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}
6
répondu senderle 2011-07-31 04:34:09

N'importe quelle solution va prendre du temps pour calculer, que ce soit par deux passes à travers la liste ou une passe qui fait plus de travail par item. La manière la plus simple est simplement d'utiliser les outils qui sont à votre disposition:itertools.ifilter et 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))
5
répondu Adam Rosenfield 2011-07-31 03:01:28

Il peut être fait avec un genex, de tri et d'

solution Brute-force:

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)

solution élégante:

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)))
3
répondu Ignacio Vazquez-Abrams 2011-07-31 02:15:17

assez simple, sans aucun outil extérieur:

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
2
répondu TorelTwiddler 2011-07-31 02:24:54

OK, c'est à propos des vaches :)

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)
1
répondu hanung 2011-07-31 03:09:19