删除与Python列表中的条件匹配的前N个项目
如果我有一个函数matchCondition(x)
,如何删除Python列表中匹配该条件的前n
项目?
一种解决scheme是迭代每个项目,将其标记为删除(例如,将其设置为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)
一种使用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
调用结果并直接迭代返回的生成器。
接受的答案有点太神奇了,因为我喜欢。 这里有一个stream程希望更清楚地遵循:
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的回答中的逻辑相似,但是不是每一步都检查drop count是否被超过,我们只是短路其余的循环。
注意:如果你没有可用的yield from
,只需要用另一个for循环来replaceiterator
的剩余项。
如果需要突变:
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)))
使用列表parsing:
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个元素后,也会停止检查状态。