10 votes

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

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

Je pourrais facilement avoir deux compréhensions de liste, ou deux sélections, mais il me semble qu'il devrait y avoir un meilleur moyen de le faire qui évite d'itérer deux fois sur chaque élément.

Je pourrais le faire facilement avec une boucle for et une instruction if, mais cela nécessite environ 7 lignes de code pour une opération qui devrait être très simple.

Des idées ?

Edit :

À titre indicatif, mes deux solutions seraient les suivantes,

# 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 me disais qu'il devait y avoir quelque chose de plus élégant que ça, auquel je n'ai jamais pensé, quelque chose comme :

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

Je sais qu'il est possible de le faire en écrivant des fonctions d'ordre supérieur pour le faire à ma place, et je sais comment le faire manuellement. Je me demandais simplement s'il existait une fonction de langage super élégante pour le faire à ma place.

C'est comme si vous pouviez écrire vos propres sous-routines et autres pour faire un SELECT sur une liste, ou vous pouvez simplement dire

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

J'espérais qu'il y aurait un moyen tout aussi élégant de fendant la liste, en un seul passage

6voto

senderle Points 41607

Que diriez-vous de 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}

5voto

Adam Rosenfield Points 176408

Toute solution prendra un temps O(N) pour être calculée, que ce soit en passant deux fois par la liste ou en passant une fois par élément. Le plus simple est d'utiliser les outils à votre disposition : itertools.ifilter y 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))

3voto

On peut le faire avec un genex, un tri et itertools.groupby() mais elle ne sera probablement pas beaucoup plus efficace que la solution par force brute.

Une solution brutale :

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)

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

3voto

eryksun Points 10251

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}

2voto

TorelTwiddler Points 2042

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X