59 votes

Supprimer les premiers N éléments correspondant à une condition dans une liste Python

Si j'ai une fonction matchCondition(x) , comment puis-je supprimer les premiers éléments n d'une liste Python qui correspondent à cette condition?

Une solution consiste à parcourir chaque élément, à le marquer pour le supprimer (par exemple, en le définissant sur None ), puis à filtrer la liste avec une compréhension. Cela nécessite de parcourir deux fois la liste et de transformer les données. Existe-t-il un moyen plus idiomatique ou plus efficace de procéder?

 n = 3

def condition(x):
    return x < 5

data = [1, 10, 2, 9, 3, 8, 4, 7]
out = do_remove(data, n, condition)
print(out)  # [10, 9, 8, 4, 7] (1, 2, and 3 are removed, 4 remains)
 

63voto

Jon Clements Points 51556

One way using itertools.filterfalse et itertools.count :

 from itertools import count, filterfalse

data = [1, 10, 2, 9, 3, 8, 4, 7]
output = filterfalse(lambda L, c=count(): L < 5 and next(c) < 3, data)
 

Alors list(output) , vous donne:

 [10, 9, 8, 4, 7]
 

29voto

davidism Points 5946

Écrire un générateur qui prend la itératif, une condition, un montant en baisse. Itérer sur les données et le rendement des éléments qui ne satisfont pas la condition. Si la condition est remplie, incrémenter un compteur et ne produisent pas la valeur. Toujours produire des articles une fois que le compteur atteint le montant que vous souhaitez déposer.

def iter_drop_n(data, condition, drop):
    dropped = 0

    for item in data:
        if dropped >= drop:
            yield item
            continue

        if condition(item):
            dropped += 1
            continue

        yield item

data = [1, 10, 2, 9, 3, 8, 4, 7]
out = list(iter_drop_n(data, lambda x: x < 5, 3))

Cela ne nécessite pas une copie supplémentaire de la liste, uniquement itère sur la liste une fois, et n'appelle qu'à la condition qu'une fois pour chaque élément. Sauf si vous voulez vraiment voir toute la liste, laisser de côté l' list appel sur le résultat et de faire une itération sur le retour de l'générateur directement.

24voto

wim Points 35274

L'on a accepté la réponse a été un peu trop magique à mon goût. Voici un endroit où le flux est, espérons un peu plus clair à suivre:

def matchCondition(x):
    return x < 5


def my_gen(L, drop_condition, max_drops=3):
    count = 0
    iterator = iter(L)
    for element in iterator:
        if drop_condition(element):
            count += 1
            if count >= max_drops:
                break
        else:
            yield element
    yield from iterator


example = [1, 10, 2, 9, 3, 8, 4, 7]

print(list(my_gen(example, drop_condition=matchCondition)))

Il est similaire à la logique dans davidism réponse, mais au lieu de vérifier la baisse du nombre est dépassé sur chaque étape, nous venons de court-circuit le reste de la boucle.

Remarque: Si vous n'avez pas yield from disponible, il suffit de remplacer par un autre pour une boucle sur les éléments restants, en iterator.

4voto

ferhat elmas Points 1465

Si une mutation est requise:

 def do_remove(ls, N, predicate):
    i, delete_count, l = 0, 0, len(ls)
    while i < l and delete_count < N:
        if predicate(ls[i]):
           ls.pop(i) # remove item at i
           delete_count, l = delete_count + 1, l - 1 
        else:
           i += 1
    return ls # for convenience

assert(do_remove(l, N, matchCondition) == [10, 9, 8, 4, 7])
 

2voto

CoDEmanX Points 937

Python simple:

 N = 3
data = [1, 10, 2, 9, 3, 8, 4, 7]

def matchCondition(x):
    return x < 5

c = 1
l = []
for x in data:
    if c > N or not matchCondition(x):
        l.append(x)
    else:
        c += 1

print(l)
 

Cela peut facilement être transformé en générateur si vous le souhaitez:

 def filter_first(n, func, iterable):
    c = 1
    for x in iterable:
        if c > n or not func(x):
            yield x
        else:
            c += 1

print(list(filter_first(N, matchCondition, data)))
 

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