Python: diviser une liste en fonction d'une condition?

Quelle est la meilleure façon, à la fois esthétiquement et du point de vue de la performance, de diviser une liste d'éléments en plusieurs listes basées sur un conditionnel? L'équivalent de:

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

Y a-t-il une façon plus élégante de le faire?

Update: voici le cas d'utilisation réel, pour mieux expliquer ce que j'essaie de faire:

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]
203
demandé sur Parand 2009-06-04 11:37:18

27 réponses

good = [x for x in mylist if x in goodvals]
bad  = [x for x in mylist if x not in goodvals]

Y a-t-il une façon plus élégante de le faire?

Ce code est parfaitement lisible, et extrêmement clair!

# files looks like: [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ... ]
IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
images = [f for f in files if f[2].lower() in IMAGE_TYPES]
anims  = [f for f in files if f[2].lower() not in IMAGE_TYPES]

Encore une fois, c'est très bien!

Il peut y avoir de légères améliorations de performances en utilisant des ensembles, mais c'est une différence triviale, et je trouve la compréhension de la liste beaucoup plus facile à lire, et vous n'avez pas à vous soucier de l'ordre étant foiré, les doublons étant supprimés comme ainsi de suite.

En fait, je peux faire un autre pas "en arrière", et utilisez simplement une boucle for simple:

images, anims = [], []

for f in files:
    if f.lower() in IMAGE_TYPES:
        images.append(f)
    else:
        anims.append(f)

La compréhension d'une liste ou l'utilisation de set() est correcte jusqu'à ce que vous ayez besoin d'ajouter une autre vérification ou un autre peu de logique - disons que vous voulez supprimer tous les jpeg de 0 octet, vous ajoutez juste quelque chose comme..

if f[1] == 0:
    continue
85
répondu dbr 2009-06-04 13:28:23
good, bad = [], []
for x in mylist:
    (bad, good)[x in goodvals].append(x)
158
répondu John La Rooy 2012-08-27 00:51:47

Voici l'approche paresseuse de l'itérateur:

from itertools import tee

def split_on_condition(seq, condition):
    l1, l2 = tee((condition(item), item) for item in seq)
    return (i for p, i in l1 if p), (i for p, i in l2 if not p)

Il évalue la condition une fois par élément et renvoie deux générateurs, d'abord en donnant des valeurs de la séquence où la condition est vraie, l'autre où elle est fausse.

Parce que c'est paresseux, vous pouvez l'utiliser sur n'importe quel itérateur, même infini:

from itertools import count, islice

def is_prime(n):
    return n > 1 and all(n % i for i in xrange(2, n))

primes, not_primes = split_on_condition(count(), is_prime)
print("First 10 primes", list(islice(primes, 10)))
print("First 10 non-primes", list(islice(not_primes, 10)))

Habituellement, bien que l'approche de retour de liste non-paresseuse soit meilleure:

def split_on_condition(seq, condition):
    a, b = [], []
    for item in seq:
        (a if condition(item) else b).append(item)
    return a, b

Edit: pour votre cas d'utilisation plus spécifique de diviser des éléments en différentes listes par une clé, voici une fonction générique qui fait cela:

DROP_VALUE = lambda _:_
def split_by_key(seq, resultmapping, keyfunc, default=DROP_VALUE):
    """Split a sequence into lists based on a key function.

        seq - input sequence
        resultmapping - a dictionary that maps from target lists to keys that go to that list
        keyfunc - function to calculate the key of an input value
        default - the target where items that don't have a corresponding key go, by default they are dropped
    """
    result_lists = dict((key, []) for key in resultmapping)
    appenders = dict((key, result_lists[target].append) for target, keys in resultmapping.items() for key in keys)

    if default is not DROP_VALUE:
        result_lists.setdefault(default, [])
        default_action = result_lists[default].append
    else:
        default_action = DROP_VALUE

    for item in seq:
        appenders.get(keyfunc(item), default_action)(item)

    return result_lists

Utilisation:

def file_extension(f):
    return f[2].lower()

split_files = split_by_key(files, {'images': IMAGE_TYPES}, keyfunc=file_extension, default='anims')
print split_files['images']
print split_files['anims']
92
répondu Ants Aasma 2015-09-22 09:59:40

Le problème avec toutes les solutions proposées est qu'il va scanner et appliquer la fonction de filtrage deux fois. Je ferais une petite fonction simple comme ceci:

def SplitIntoTwoLists(l, f):
  a = []
  b = []
  for i in l:
    if f(i):
      a.append(i)
    else:
      b.append(i)
 return (a,b)

De cette façon, vous ne traitez rien deux fois et ne répétez pas non plus le code.

22
répondu winden 2009-06-04 08:26:26

Mon point de vue. Je propose une fonction paresseuse, en un seul passage, partition, qui préserve l'ordre relatif dans les sous-séquences de sortie.

1. Exigences

Je suppose que les exigences sont:

  • maintient l'ordre relatif des éléments (par conséquent, aucun les dictionnaires)
  • évaluer la condition une seule fois pour chaque élément (donc ne pas utiliser (i)filter ou groupby)
  • permettre une consommation paresseuse de l'une ou l'autre séquence (si nous pouvons nous le permettre précalculer, l' naïf mise en œuvre est susceptible d'être acceptable aussi)

2. split bibliothèque

Ma fonction partition (présentée ci-dessous) et d'autres fonctions similaires l'ont fait dans une petite bibliothèque:

Il est installable normalement via PyPI:

pip install --user split

Pour diviser une base de liste sur condition, utilisez la fonction partition:

>>> from split import partition
>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi') ]
>>> image_types = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> images, other = partition(lambda f: f[-1] in image_types, files)
>>> list(images)
[('file1.jpg', 33L, '.jpg')]
>>> list(other)
[('file2.avi', 999L, '.avi')]

3. partition fonction expliquée

En interne, nous devons construire deux sous-séquences à la fois, donc consommer une seule séquence de sortie forcera l'autre à être calculée trop. Et nous devons garder l'état entre les demandes des utilisateurs (magasin traité mais pas encore demandé d'éléments). Pour garder l'état, j'utilise deux doubles extrémités les files d'attente (deques):

from collections import deque

SplitSeq la classe s'occupe du ménage:

class SplitSeq:
    def __init__(self, condition, sequence):
        self.cond = condition
        self.goods = deque([])
        self.bads = deque([])
        self.seq = iter(sequence)

La magie se produit dans sa méthode .getNext(). C'est presque comme .next() des itérateurs, mais permet de spécifier quel type d'élément nous voulons cette fois. Derrière la scène il ne jette pas le rejeté des éléments de, mais à la place les met dans l'une des deux files d'attente:

    def getNext(self, getGood=True):
        if getGood:
            these, those, cond = self.goods, self.bads, self.cond
        else:
            these, those, cond = self.bads, self.goods, lambda x: not self.cond(x)
        if these:
            return these.popleft()
        else:
            while 1: # exit on StopIteration
                n = self.seq.next()
                if cond(n):
                    return n
                else:
                    those.append(n)

L'utilisateur final est censé utiliser la fonction partition. Il faut un fonction de condition et une séquence (tout comme map ou filter), et retourne deux générateurs. Le premier générateur construit une sous-séquence de éléments pour lesquels la condition est valable, le second construit le sous-séquence complémentaire. Itérateurs et générateurs permettent pour paresseux fractionnement de séquences même longues ou infinies.

def partition(condition, sequence):
    cond = condition if condition else bool  # evaluate as bool if condition == None
    ss = SplitSeq(cond, sequence)
    def goods():
        while 1:
            yield ss.getNext(getGood=True)
    def bads():
        while 1:
            yield ss.getNext(getGood=False)
    return goods(), bads()

J'ai choisi le fonction de test pour être le premier argument pour faciliter application partielle à l'avenir (similaire à comment map et filter avoir la fonction de test comme premier argument).

18
répondu sastanin 2017-05-10 23:22:00

Tout d'abord aller (pre-OP-edit): utiliser les ensembles:

mylist = [1,2,3,4,5,6,7]
goodvals = [1,3,7,8,9]

myset = set(mylist)
goodset = set(goodvals)

print list(myset.intersection(goodset))  # [1, 3, 7]
print list(myset.difference(goodset))    # [2, 4, 5, 6]

C'est bon pour la lisibilité (à mon humble avis) et la performance.

Deuxième aller (post-OP-edit):

Créez votre liste de bonnes extensions en tant qu'ensemble:

IMAGE_TYPES = set(['.jpg','.jpeg','.gif','.bmp','.png'])

Et cela augmentera les performances. Sinon, ce que vous avez me semble bien.

13
répondu RichieHindle 2009-06-04 07:54:01

J'aime fondamentalement l'approche D'Anders car elle est très générale. Voici une version qui met le catégoriseur en premier (pour correspondre à la syntaxe du filtre) et utilise un defaultdict (supposé importé).

def categorize(func, seq):
    """Return mapping from categories to lists
    of categorized items.
    """
    d = defaultdict(list)
    for item in seq:
        d[func(item)].append(item)
    return d
13
répondu Alan Isaac 2010-07-19 14:20:02

Itertools.groupby fait presque ce que vous voulez, sauf qu'il nécessite que les éléments soient triés pour vous assurer d'obtenir une seule plage contiguë, donc vous devez d'abord Trier par votre clé (sinon vous obtiendrez plusieurs groupes entrelacés pour chaque type). par exemple.

def is_good(f):
    return f[2].lower() in IMAGE_TYPES

files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file3.gif', 123L, '.gif')]

for key, group in itertools.groupby(sorted(files, key=is_good), key=is_good):
    print key, list(group)

Donne:

False [('file2.avi', 999L, '.avi')]
True [('file1.jpg', 33L, '.jpg'), ('file3.gif', 123L, '.gif')]

Semblable aux autres solutions, la fonction clé peut être définie pour diviser en un nombre quelconque de groupes que vous voulez.

8
répondu Brian 2009-06-04 13:34:29

Personnellement, j'aime la version que vous avez Citée, en supposant que vous avez déjà une liste de goodvals traîner. Si non, quelque chose comme:

good = filter(lambda x: is_good(x), mylist)
bad = filter(lambda x: not is_good(x), mylist)

Bien sûr, c'est vraiment très similaire à l'utilisation d'une compréhension de liste comme vous l'a fait à l'origine, mais avec une fonction au lieu d'une recherche:

good = [x for x in mylist if is_good(x)]
bad  = [x for x in mylist if not is_good(x)]

En général, je trouve l'esthétique des compréhensions de liste très agréable. Bien sûr, si vous n'avez pas réellement besoin de conserver la commande et n'avez pas besoin de doublons, utilisez les intersection et difference les méthodes sur les ensembles fonctionneraient bien aussi.

5
répondu BJ Homer 2015-01-27 21:42:52

Si vous voulez le faire dans le style FP:

good, bad = [ sum(x, []) for x in zip(*(([y], []) if y in goodvals else ([], [y])
                                        for y in mylist)) ]

Pas la solution la plus lisible, mais au moins itère à travers mylist une seule fois.

4
répondu michau 2010-05-10 00:28:43
def partition(pred, iterable):
    'Use a predicate to partition entries into false entries and true entries'
    # partition(is_odd, range(10)) --> 0 2 4 6 8   and  1 3 5 7 9
    t1, t2 = tee(iterable)
    return filterfalse(pred, t1), filter(pred, t2)

Vérifier ce

3
répondu Hanfei Sun 2015-02-24 09:26:53

Je pense qu'une généralisation de la division d'un itérable basé sur N conditions est pratique

from collections import OrderedDict
def partition(iterable,*conditions):
    '''Returns a list with the elements that satisfy each of condition.
       Conditions are assumed to be exclusive'''
    d= OrderedDict((i,list())for i in range(len(conditions)))        
    for e in iterable:
        for i,condition in enumerate(conditions):
            if condition(e):
                d[i].append(e)
                break                    
    return d.values()

Par exemple:

ints,floats,other = partition([2, 3.14, 1, 1.69, [], None],
                              lambda x: isinstance(x, int), 
                              lambda x: isinstance(x, float),
                              lambda x: True)

print " ints: {}\n floats:{}\n other:{}".format(ints,floats,other)

 ints: [2, 1]
 floats:[3.14, 1.69]
 other:[[], None]

Si l'élément peut satisfaire à plusieurs conditions, supprimer la rupture.

3
répondu Gecko 2015-05-01 16:08:09

Parfois, il semble que la compréhension de liste n'est pas la meilleure chose à utiliser !

J'ai fait un petit test basé sur la réponse que les gens ont donnée à ce sujet, testé sur une liste générée au hasard. Voici la génération de la liste (il y a probablement une meilleure façon de le faire, mais ce n'est pas le point):

good_list = ('.jpg','.jpeg','.gif','.bmp','.png')

import random
import string
my_origin_list = []
for i in xrange(10000):
    fname = ''.join(random.choice(string.lowercase) for i in range(random.randrange(10)))
    if random.getrandbits(1):
        fext = random.choice(good_list)
    else:
        fext = "." + ''.join(random.choice(string.lowercase) for i in range(3))

    my_origin_list.append((fname + fext, random.randrange(1000), fext))

Et voilà

# Parand
def f1():
    return [e for e in my_origin_list if e[2] in good_list], [e for e in my_origin_list if not e[2] in good_list]

# dbr
def f2():
    a, b = list(), list()
    for e in my_origin_list:
        if e[2] in good_list:
            a.append(e)
        else:
            b.append(e)
    return a, b

# John La Rooy
def f3():
    a, b = list(), list()
    for e in my_origin_list:
        (b, a)[e[2] in good_list].append(e)
    return a, b

# Ants Aasma
def f4():
    l1, l2 = tee((e[2] in good_list, e) for e in my_origin_list)
    return [i for p, i in l1 if p], [i for p, i in l2 if not p]

# My personal way to do
def f5():
    a, b = zip(*[(e, None) if e[2] in good_list else (None, e) for e in my_origin_list])
    return list(filter(None, a)), list(filter(None, b))

# BJ Homer
def f6():
    return filter(lambda e: e[2] in good_list, my_origin_list), filter(lambda e: not e[2] in good_list, my_origin_list)

En utilisant la fonction cmpthese, le meilleur résultat est la réponse dbr:

f1     204/s  --    -5%   -14%   -15%   -20%   -26%
f6     215/s     6%  --    -9%   -11%   -16%   -22%
f3     237/s    16%    10%  --    -2%    -7%   -14%
f4     240/s    18%    12%     2%  --    -6%   -13%
f5     255/s    25%    18%     8%     6%  --    -8%
f2     277/s    36%    29%    17%    15%     9%  --
2
répondu FunkySayu 2015-07-16 08:12:37

Encore une autre solution à ce problème. J'ai besoin d'une solution qui est aussi rapide que possible. Cela signifie une seule itération sur la liste et de préférence O (1) pour ajouter des données à l'une des listes résultantes. Ceci est très similaire à la solution fournie par sastanin, sauf beaucoup plus courte:

from collections import deque

def split(iterable, function):
    dq_true = deque()
    dq_false = deque()

    # deque - the fastest way to consume an iterator and append items
    deque((
      (dq_true if function(item) else dq_false).append(item) for item in iterable
    ), maxlen=0)

    return dq_true, dq_false

Ensuite, vous pouvez utiliser la fonction de la manière suivante:

lower, higher = split([0,1,2,3,4,5,6,7,8,9], lambda x: x < 5)

selected, other = split([0,1,2,3,4,5,6,7,8,9], lambda x: x in {0,4,9})

Si vous n'êtes pas d'accord avec l'objet deque résultant, vous pouvez facilement le convertir en list, set, comme tu veux (par exemple list(lower)). La conversion est beaucoup plus rapide, que la construction des listes directement.

Cette méthode conserve l'ordre des éléments, ainsi que tous les doublons.

2
répondu rlat 2015-07-22 11:58:18

Pour la performance, essayez itertools.

Le module itertools standardise un ensemble de base d'outils rapides et efficaces en mémoire qui sont utiles seuls ou en combinaison. Ensemble, ils forment une "algèbre d'itérateur" permettant de construire des outils spécialisés de manière succincte et efficace en Python pur.

Voir itertools.ifilter {[6] } ou imap.

Itertools.ifilter (prédicat, itérable)

Créez un itérateur qui filtre les éléments de iterable ne renvoyant que ceux pour lesquels le prédicat est vrai

1
répondu gimel 2009-06-04 07:51:54

Parfois, vous n'aurez pas besoin de cette autre moitié de la liste. Par exemple:

import sys
from itertools import ifilter

trustedPeople = sys.argv[1].split(',')
newName = sys.argv[2]

myFriends = ifilter(lambda x: x.startswith('Shi'), trustedPeople)

print '%s is %smy friend.' % (newName, newName not in myFriends 'not ' or '')
1
répondu Shikhar Mall 2011-05-02 16:33:47

Si vous insistez sur clever, vous pourriez prendre la solution de Winden et juste un peu fausse intelligence:

def splay(l, f, d=None):
  d = d or {}
  for x in l: d.setdefault(f(x), []).append(x)
  return d
0
répondu Anders Eurenius 2009-06-04 12:25:01

Si votre préoccupation est de ne pas utiliser deux lignes de code pour une opération dont la sémantique n'a besoin qu'une fois que vous enveloppez certaines des approches ci-dessus (même les vôtres) dans une seule fonction:

def part_with_predicate(l, pred):
    return [i for i in l if pred(i)], [i for i in l if not pred(i)]

Ce n'est pas une approche paresseuse et elle parcourt deux fois la liste, mais elle vous permet de partitionner la liste en une seule ligne de code.

0
répondu jcb 2012-05-04 20:53:55

Inspiré par @gnibbler's génial (mais laconique!) réponse , Nous pouvons appliquer cette approche à la carte à plusieurs partitions:

from collections import defaultdict

def splitter(l, mapper):
    """Split an iterable into multiple partitions generated by a callable mapper."""

    results = defaultdict(list)

    for x in l:
        results[mapper(x)].append(x)

    return results

Alors splitter peut alors être utilisé comme suit:

>>> l = [1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 3, 2, 3]
>>> split = splitter(l, lambda x: x % 2 == 0)  # partition l into odds and evens
>>> split.items()
>>> [(False, [1, 3, 3, 5, 3, 3]), (True, [2, 4, 2, 4, 6, 4, 2])]

Cela fonctionne pour plus de deux partitions avec un mappage plus compliqué (et sur les itérateurs aussi):

>>> import math
>>> l = xrange(1, 23)
>>> split = splitter(l, lambda x: int(math.log10(x) * 5))
>>> split.items()
[(0, [1]),
 (1, [2]),
 (2, [3]),
 (3, [4, 5, 6]),
 (4, [7, 8, 9]),
 (5, [10, 11, 12, 13, 14, 15]),
 (6, [16, 17, 18, 19, 20, 21, 22])]

Ou utiliser un dictionnaire pour mapper:

>>> map = {'A': 1, 'X': 2, 'B': 3, 'Y': 1, 'C': 2, 'Z': 3}
>>> l = ['A', 'B', 'C', 'C', 'X', 'Y', 'Z', 'A', 'Z']
>>> split = splitter(l, map.get)
>>> split.items()
(1, ['A', 'Y', 'A']), (2, ['C', 'C', 'X']), (3, ['B', 'Z', 'Z'])]
0
répondu Josh Bode 2017-05-23 12:26:20

Déjà quelques solutions ici, mais encore une autre façon de le faire serait -

anims = []
images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]

Itère sur la liste une seule fois, et me semble un peu plus pythonique et donc lisible.

>>> files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi'), ('file1.bmp', 33L, '.bmp')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
>>> anims = []
>>> images = [f for f in files if (lambda t: True if f[2].lower() in IMAGE_TYPES else anims.append(t) and False)(f)]
>>> print '\n'.join([str(anims), str(images)])
[('file2.avi', 999L, '.avi')]
[('file1.jpg', 33L, '.jpg'), ('file1.bmp', 33L, '.bmp')]
>>>
0
répondu Shreyas 2014-03-08 03:40:43

Je prendrais une approche à 2 passes, séparant l'évaluation du prédicat du filtrage de la liste:

def partition(pred, iterable):
    xs = list(zip(map(pred, iterable), iterable))
    return [x[1] for x in xs if x[0]], [x[1] for x in xs if not x[0]]

Ce qui est bien à ce sujet, en termes de performances (en plus d'évaluer pred une seule fois sur chaque membre de iterable), c'est qu'il déplace beaucoup de logique hors de l'interpréteur et dans un code d'itération et de mappage hautement optimisé. Cela peut accélérer l'itération sur de longues itérables, comme décrit dans cette réponse .

Expressivité-sage, Il tire parti des idiomes expressifs comme compréhensions et cartographie.

0
répondu Jim Witschey 2017-05-23 12:34:37

Solution

from itertools import tee

def unpack_args(fn):
    return lambda t: fn(*t)

def separate(fn, lx):
    return map(
        unpack_args(
            lambda i, ly: filter(
                lambda el: bool(i) == fn(el),
                ly)),
        enumerate(tee(lx, 2)))

Essai

[even, odd] = separate(
    lambda x: bool(x % 2),
    [1, 2, 3, 4, 5])
print(list(even) == [2, 4])
print(list(odd) == [1, 3, 5])
0
répondu doctorzeb8 2015-04-28 07:49:17

Ma recette préférée pour cela est:

goodvals = set(goodvals)    
good, bad = [], []
_ = [good.append(x) if x in goodvals else bad.append(x) for x in mylist]

Simple, rapide et lisible; la façon dont Python était censé être.

  • En faisant goodvals en set (qui utilise une table de hachage) au lieu d'un tuple, nous obtenons des recherches super rapides.
  • Chaque élément de mylist est vérifiée uniquement lorsque. cela aide à le rendre plus rapide.
  • _ = est un moyen pythonique de déclarer que nous rejetons le résultat de la compréhension de la liste exprès. Ce n'est pas un bug.

(basé sur dansalmo commentez à cette réponse , car elle semble mériter d'être sa propre réponse.)

0
répondu ChaimG 2017-05-23 12:18:17

Si cela ne vous dérange pas d'utiliser une bibliothèque externe là-bas deux je sais que nativly implémenter cette opération:

>>> files = [ ('file1.jpg', 33, '.jpg'), ('file2.avi', 999, '.avi')]
>>> IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
  • iteration_utilities.partition:

    >>> from iteration_utilities import partition
    >>> notimages, images = partition(files, lambda x: x[2].lower() in IMAGE_TYPES)
    >>> notimages
    [('file2.avi', 999, '.avi')]
    >>> images
    [('file1.jpg', 33, '.jpg')]
    
  • more_itertools.partition

    >>> from more_itertools import partition
    >>> notimages, images = partition(lambda x: x[2].lower() in IMAGE_TYPES, files)
    >>> list(notimages)  # returns a generator so you need to explicitly convert to list.
    [('file2.avi', 999, '.avi')]
    >>> list(images)
    [('file1.jpg', 33, '.jpg')]
    
0
répondu MSeifert 2017-04-04 19:57:30

Ne sais Pas si c'est une bonne approche, mais il peut être fait de cette façon

IMAGE_TYPES = ('.jpg','.jpeg','.gif','.bmp','.png')
files = [ ('file1.jpg', 33L, '.jpg'), ('file2.avi', 999L, '.avi')]
images, anims = reduce(lambda (i, a), f: (i + [f], a) if f[2] in IMAGE_TYPES else (i, a + [f]), files, ([], []))
0
répondu kiran 2017-10-12 13:45:39

Par exemple, diviser la liste par Pair et Impair

arr = range(20)
even, odd = reduce(lambda res, next: res[next % 2].append(next) or res, arr, ([], []))

Ou en général:

def split(predicate, iterable):
    return reduce(lambda res, e: res[predicate(e)].append(e) or res, iterable, ([], []))

Avantages:

  • la plus courte possible, de manière
  • le prédicat s'applique une seule fois pour chaque élément

Inconvénients

  • nécessite la connaissance du paradigme de programmation fonctionnelle
0
répondu Pavel Ilchenko 2018-10-01 08:07:06
def partition(pred, seq):
  return reduce( lambda (yes, no), x: (yes+[x], no) if pred(x) else (yes, no+[x]), seq, ([], []) )
-1
répondu haavee 2013-12-10 10:56:11