繁体   English   中英

删除与Python列表中的条件匹配的前N个项

[英]Remove the first N items that match a condition in a Python list

如果我有一个函数matchCondition(x) ,我怎样才能删除匹配该条件的Python列表中的前n项?

一种解决方案是迭代每个项目,将其标记为删除(例如,通过将其设置为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.filterfalseitertools.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)))

它类似于大卫答案中的逻辑,但是不是检查每一步的丢弃计数,我们只是将循环的其余部分短路。

注意:如果没有可用的yield from ,只需用另一个替换for循环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个元素后停止检查条件。

启动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
    • 如果我们还没有丢弃超过我们想要过滤的项目数量:
      • 通过赋值表达式递增totaltotal := total + 1
      • 同时将total价值的新值与丢弃的最大物品数量进行比较( 3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM