Удалите первые N элементов, которые соответствуют условию в списке Python
если у меня есть функция matchCondition(x)
, Как я могу удалить первый n
элементы в списке Python, соответствующие этому условию?
одним из решений является итерация по каждому элементу, отметьте его для удаления (например, установив его в None
), а затем фильтровать список с пониманием. Это требует повторения списка дважды и изменяет данные. Есть ли более идиоматические или эффективный способ сделать это?
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)
6 ответов:
один из способов, используя
itertools.filterfalse
и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)
затем
list(output)
, дает вам:[10, 9, 8, 4, 7]
напишите генератор, который принимает итерацию, условие и сумму для удаления. Повторите данные и получите элементы, которые не соответствуют условию. Если условие выполнено, увеличьте счетчик и не выдавайте значение. Всегда отдавайте предметы, как только счетчик достигнет суммы, которую вы хотите сбросить.
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))
это не требует дополнительной копии списка, только повторяет список один раз, и только вызывает условие один раз для каждого элемента. Если вы действительно хотите видеть весь список, оставьте
list
вызовите результат и выполните итерацию по возвращенному генератору напрямую.
принятый ответ был слишком волшебным на мой вкус. Вот один, где поток, надеюсь, немного яснее, чтобы следовать:
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)))
Это похоже на логику в davidism ответ, но вместо того, чтобы проверять количество падений превышается на каждом шаге, мы просто закорачиваем остальную часть цикла.
Примечание: если у вас нет
yield from
доступный, как раз замените его с другим для петли над остальными деталями внутриiterator
.
если требуется мутация:
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:
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)
Это можно легко превратить в генератор при желании:
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)))
использовать список осмысленностей:
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)]
это также прекратит проверку состояния после n элементы найдены благодаря логическому короткому замыканию.