查找与条件匹配的行中的最后一个元素

Posted

技术标签:

【中文标题】查找与条件匹配的行中的最后一个元素【英文标题】:Find the last element in a row that matches a criterion 【发布时间】:2020-02-23 10:23:46 【问题描述】:

除了这个问题“Get the first item from an iterable that matches a condition”之外,我还需要第一个匹配之前的元素。或完全等价:一行中与条件匹配的最后一个元素。例如,如果我需要此列表中第一个非素数之前的最后一个素数 [2, 3, 4, 5, 6, 7, 8, 9],则答案是 3。如果没有这样的元素,结果可能是 None、异常或默认值。

直接的解决方案是:

result = None
for x in numbers:
    if is_prime(x):
        result = x
    else:
        break

是否有一个简单的单线可以解决任务?

【问题讨论】:

【参考方案1】:

不确定是否有更简单的方法。我的方法需要itertools.takewhile

    使用itertools.takewhile在不再满足条件后停止迭代以创建素数列表直到第一个非素数出现,即[2, 3] 然后使用list[-1] 获取列表的最后一个元素。 如果列表为空,请使用(list or [None])[-1],以便返回None
import itertools
result = ([x for x in itertools.takewhile(lambda n: is_prime(n), numbers)] or [None])[-1]

collections.deque()

from collections import deque
result = (deque((itertools.takewhile(lambda n: is_prime(n), numbers)), maxlen=1) or [None]).pop()

【讨论】:

此解决方案从生成器创建完整列表。失去可读性我们也失去了效率。理想的解决方案应该只保留以前和当前的元素。【参考方案2】:

您可以使用zipislice 构造一个迭代器,它有自己的“未来”可用。

next(
    filter(
        lambda pair: isprime(pair[1]),  # The predicate applied to a pair
        zip(xs, islice(xs, 1, None))  # The iterator and its 'future'
    ),
    (None, None)  # A default pair in case no matches are found
)[0]  # Retrieve the 'current' entry from the matching pair

在每个步骤中,您都有一对您可以认为是(present, future),谓词将应用于future。最后,我们解包present,它表示迭代器中最后一个不匹配的条目。

请注意,如所示,此实现不会返回列表中的最后一个条目,因为本质上,如果您将[a, b, c] 压缩到其自己的移位版本,您最终会得到一个比另一个短的。你可以使用zip_longest(来自itertools)来克服这个问题,但你需要在你的谓词中处理fillvalue(通常是None)。


正如评论中所述,这对生成器不起作用,因为它会消耗生成器。但是,使用折叠(Python 中的reduce)会变得更容易。首先,在更“Pythonic”的演示中,函数看起来像这样:

def fold(hist, cur):
    stop, prev = hist

    if stop:
        return hist

    if isprime(cur):
        return (True, prev)

    return (False, cur)

元组的第一个元素用作“停止”标记,第二个元素是我们的“针”。您可以将其与 reduce 一起使用:

reduce(fold, xs, (False, None))[1]

这并不完全是“一个衬里”,但我们可以将其压缩成lambda

reduce(lambda z, x: (z if z[0] else ((1, z[1]) if isprime(x) else (0, x))), xs, (0, None))[1]

【讨论】:

有趣的解决方案。更接近我的预期。然而,这对生成器不起作用,但如果将表达式 zip(xs, islice(xs, 1, None)) 替换为仅接受一次 xs 的东西,那将起作用。 你可以通过折叠来做到这一点。我会将其添加到答案中。

以上是关于查找与条件匹配的行中的最后一个元素的主要内容,如果未能解决你的问题,请参考以下文章

Groovy集合遍历 ( 使用集合的 find 方法查找集合元素 | 闭包中使用 == 作为查找匹配条件 | 闭包中使用 is 作为查找匹配条件 | 闭包使用 true 作为条件 | 代码示例 )

Groovy集合遍历 ( 使用集合的 find 方法查找集合元素 | 闭包中使用 == 作为查找匹配条件 | 闭包中使用 is 作为查找匹配条件 | 闭包使用 true 作为条件 | 代码示例 )(代

有条件地显示具有匹配值的单元格的行中的值

查找第一个元素最近的行

3.05 在一个表中查找与其他表不匹配的记录

Pandas:在多列中查找具有匹配值的行的 Pythonic 方法(分层条件)