Удалите первые 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)
Ответы
Ответ 1
Один из способов: 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]
Ответ 2
Напишите генератор, который принимает итерабельность, условие и количество, которое нужно отбросить. Итерации по данным и выводятся элементы, которые не соответствуют этому условию. Если условие выполнено, увеличьте счетчик и не получите значение. Всегда возвращайте предметы, как только счетчик достигнет суммы, которую вы хотите сбросить.
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
на результат и перейдем к возвращенному генератору напрямую.
Ответ 3
Принятый ответ был слишком волшебным для моей симпатии. Здесь, где поток, как мы надеемся, будет более понятным:
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
.
Ответ 4
Если необходима мутация:
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])
Ответ 5
Простой 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)))
Ответ 6
Использование списков:
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 элементов будут найдены благодаря булевому короткому замыканию.
Ответ 7
Начиная с Python 3.8
и введением выражений присваивания (PEP 572) (:=
оператор), мы можем использовать и увеличивать переменную в пределах понимания списка:
# 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]
Это:
- Инициализирует переменную
total
до 0
которая будет символизировать количество ранее сопоставленных вхождений в пределах понимания списка - Проверяет каждый элемент, если он оба:
- соответствует условию исключения (
x < 5
) - и если мы еще не отбросили больше, чем количество элементов, по которым мы хотели бы отфильтровать:
- увеличение
total
(total := total + 1
) с помощью выражения присваивания - и в то же время сравнивая новое
total
значение с максимальным количеством предметов для отбрасывания (3
)