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 suppression (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)
Une manière utilisant 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]
Ecrivez un générateur qui prend l'itérable, une condition et un montant à supprimer. Parcourez les données et renvoyez les éléments qui ne répondent pas à la condition. Si la condition est remplie, incrémentez un compteur et ne renvoyez pas la valeur. Cédez toujours les objets 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 de copie supplémentaire de la liste, il suffit de parcourir la liste une seule fois et d'appeler la condition une seule fois pour chaque élément. À moins que vous ne souhaitiez voir toute la liste, laissez l'appel list
sur le résultat et parcourez directement le générateur retourné.
La réponse acceptée était un peu trop magique à mon goût. En voici un où le flux est, espérons-le, 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)))
Cela ressemble à la logique dans davidism answer, mais au lieu de vérifier que le nombre de gouttes est dépassé à chaque étape, nous ne faisons que court-circuiter le reste de la boucle.
Remarque: Si vous n'avez pas yield from
, remplacez-le simplement par une autre boucle for sur les éléments restants dans iterator
.
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])
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)))
À partir de Python 3.8
et de l'introduction des expressions d'affectation (PEP 572) (opérateur :=
), nous pouvons utiliser et incrémenter une variable dans une liste de compréhension:
# items = [1, 10, 2, 9, 3, 8, 4, 7]
total = 0
[x for x in items if not (x < 5 and (total := total + 1) <= 3)]
# [10, 9, 8, 4, 7]
Ce:
total
à 0
qui symbolisera le nombre d'occurrences précédemment appariées dans la compréhension de la listex < 5
)total
(total := total + 1
) via une expression d'affectation total
au nombre maximal d'éléments à ignorer (3
)Utilisation de la compréhension de liste:
n = 3
data = [1, 10, 2, 9, 3, 8, 4, 7]
count = 0
def counter(x):
global count
count += 1
return x
def condition(x):
return x < 5
filtered = [counter(x) for x in data if count < n and condition(x)]
Cela arrêtera également de vérifier la condition après que n éléments aient été trouvés grâce à un court-circuit booléen.