从匹配条件的迭代器中获取第一个项

时间:2021-10-09 21:58:33

I would like to get the first item from a list matching a condition. It's important that the resulting method not process the entire list, which could be quite large. For example, the following function is adequate:

我想从匹配条件的列表中获得第一项。重要的是,结果方法不能处理整个列表,这可能非常大。例如,下列职能是充分的:

def first(the_iterable, condition = lambda x: True):
    for i in the_iterable:
        if condition(i):
            return i

This function could be used something like this:

这个函数可以这样使用:

>>> first(range(10))
0
>>> first(range(10), lambda i: i > 3)
4

However, I can't think of a good built-in / one-liner to let me do this. I don't particularly want to copy this function around if I don't have to. Is there a built-in way to get the first item matching a condition?

但是,我想不出一个好的内置/一行程序来实现这一点。如果不需要的话,我也不想复制这个函数。是否有一种内置的方法来获取与条件匹配的第一项?

13 个解决方案

#1


281  

In Python 2.6 or better:

在Python 2.6或更好的版本中:

If you want StopIteration to be raised if no matching element is found:

如果您希望在没有找到匹配元素的情况下引发StopIteration:

next(x for x in the_iterable if x > 3)

接下来(x在the_iterable中,如果x > 3)

If you want default_value (e.g. None) to be returned instead:

如果您想要返回default_value(例如None):

next( (x for x in the_iterable if x>3), default_value)

接下来(x表示the_iterable中的x,如果x>3), default_value)

Note that you need an extra pair of parentheses around the generator expression in this case - they are needed always when the generator expression isn't the only argument.

请注意,在这种情况下,您需要在生成器表达式周围附加一对圆括号——当生成器表达式不是惟一的参数时,总是需要它们。

I see most answers resolutely ignore the next built-in and so I assume that for some mysterious reason they're 100% focused on versions 2.5 and older -- without mentioning the Python-version issue (but then I don't see that mention in the answers that do mention the next built-in, which is why I thought it necessary to provide an answer myself -- at least the "correct version" issue gets on record this way;-).

我看到大多数答案坚决忽略下一个内置的某些神秘的原因,所以我认为他们100%集中在版本2.5及以上,没有提及python版本问题(但我不看到答案,提提下一个内置的,这就是为什么我认为有必要提供答案,至少“正确版本”问题得到记录这样;-)。

In 2.5, the .next() method of iterators immediately raises StopIteration if the iterator immediately finishes -- i.e., for your use case, if no item in the iterable satisfies the condition. If you don't care (i.e., you know there must be at least one satisfactory item) then just use .next() (best on a genexp, line for the next built-in in Python 2.6 and better).

在2.5中,如果迭代器立即结束,迭代器的.next()方法会立即引发StopIteration——即。,对于您的用例,如果iterable中没有项满足条件。如果你不在乎(例如:,您知道必须至少有一个满意的项)然后使用.next()(在genexp上最好,Python 2.6中的下一个内置行更好)。

If you do care, wrapping things in a function as you had first indicated in your Q seems best, and while the function implementation you proposed is just fine, you could alternatively use itertools, a for...: break loop, or a genexp, or a try/except StopIteration as the function's body, as various answers suggested. There's not much added value in any of these alternatives so I'd go for the starkly-simple version you first proposed.

如果您确实关心这个问题,那么按照您在Q中第一次指出的那样将事情包装到函数中似乎是最好的,并且尽管您建议的函数实现很好,但是您也可以使用itertools, a for…:中断循环,或genexp,或try/except StopIteration,如各种答案所示。这些方案中没有多少附加价值,所以我将采用您最初提出的非常简单的版本。

#2


17  

As a reusable, documented and tested function

def first(iterable, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Raises `StopIteration` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    """

    return next(x for x in iterable if condition(x))

#3


13  

Similar to using ifilter, you could use a generator expression:

类似于使用ifilter,您可以使用生成器表达式:

>>> (x for x in xrange(10) if x > 5).next()
6

In either case, you probably want to catch StopIteration though, in case no elements satisfy your condition.

无论哪种情况,您都可能希望捕获StopIteration,以防没有元素满足您的条件。

Technically speaking, I suppose you could do something like this:

从技术上讲,我想你可以做这样的事情:

>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6

It would avoid having to make a try/except block. But that seems kind of obscure and abusive to the syntax.

它将避免不得不尝试[除了]块。但这似乎有点模糊和滥用语法。

#4


8  

Damn Exceptions!

I love this answer. However, since next() raise a StopIteration exception when there are no items, i would use the following snippet to avoid an exception:

我喜欢这个答案。但是,由于next()在没有项目时引发了StopIteration异常,我将使用以下代码片段来避免异常:

a = []
item = next((x for x in a), None)

For example,

例如,

a = []
item = next(x for x in a)

Will raise a StopIteration exception;

将引发StopIteration异常;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

#5


6  

For older versions of Python where the next built-in doesn't exist:

对于不存在下一个内置程序的Python的旧版本:

(x for x in range(10) if x > 3).next()

#6


6  

I would write this

我想写这

next(x for x in xrange(10) if x > 3)

#7


5  

The itertools module contains a filter function for iterators. The first element of the filtered iterator can be obtained by calling next() on it:

itertools模块包含迭代器的筛选函数。过滤迭代器的第一个元素可以通过调用next()来获得:

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()

#8


4  

By using

通过使用

(index for index, value in enumerate(the_iterable) if condition(value))

one can check the condition of the value of the first item in the_iterable, and obtain its index without the need to evaluate all of the items in the_iterable.

可以检查the_iterable中第一个项的值的条件,并获得它的索引,而不需要计算the_iterable中的所有项。

The complete expression to use is

要使用的完整表达式是

first_index = next(index for index, value in enumerate(the_iterable) if condition(value))

Here first_index assumes the value of the first value identified in the expression discussed above.

这里first_index假设在上面讨论的表达式中识别的第一个值的值。

#9


1  

Since you've requested a built-in one-liner, this will avoid the issue of a StopIteration exception, though it requires that your iterable is small so you can cast it to a list, since that is the only construct I know of which will swallow a StopIteration and let you peek at the values:

既然你要求一个内置的一行程序,这将避免抛出StopIteration异常的问题,尽管它要求你iterable很小所以你可以丢给一个列表,因为这是我所知道的唯一的构造将吞下抛出StopIteration和让你偷看的值:

(lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION))

(If no element matches, you will get None rather than a StopIteration exception.)

(如果没有元素匹配,您将得到None,而不是StopIteration异常。)

#10


1  

This question already has great answers. I'm only adding my two cents because I landed here trying to find a solution to my own problem, which is very similar to the OP.

这个问题已经有了很好的答案。我只是增加了我的2分,因为我来到这里是想找到一个解决我自己问题的方法,这和OP非常相似。

If you want to find the INDEX of the first item matching a criteria using generators, you can simply do:

如果您想使用生成器查找与标准匹配的第一个项目的索引,您只需:

next(index for index, value in enumerate(iterable) if condition)

#11


1  

The most efficient way in Python 3 are one of the following (using a similar example):

Python 3中最有效的方法是以下方法之一(使用类似的示例):

With "comprehension" style:

next(i for i in range(100000000) if i == 1000)

WARNING: The expression works also with Python 2, but in the example is used range that returns an iterable object in Python 3 instead of a list like Python 2 (if you want to construct an iterable in Python 2 use xrange instead).

警告:表达式也适用于Python 2,但是在示例中使用range返回Python 3中的可迭代对象,而不是像Python 2那样的列表(如果您想在Python 2中构造一个可迭代对象,请使用xrange)。

Note that the expression avoid to construct a list in the comprehension expression next([i for ...]), that would cause to create a list with all the elements before filter the elements, and would cause to process the entire options, instead of stop the iteration once i == 1000.

注意,表达式避免在下一个([i for…])中构造一个列表,这将导致在筛选元素之前创建一个包含所有元素的列表,并导致处理整个选项,而不是在i = 1000时停止迭代。

With "functional" style:

next(filter(lambda i: i == 1000, range(100000000)))

WARNING: This doesn't work in Python 2, even replacing range with xrange due that filter create a list instead of a iterator (inefficient), and the next function only works with iterators.

警告:这在Python 2中不起作用,甚至替换了xrange,因为该过滤器创建了一个列表而不是迭代器(效率低),下一个函数只与迭代器一起工作。

Default value

As mentioned in other responses, you must add a extra-parameter to the function next if you want to avoid an exception raised when the condition is not fulfilled.

正如在其他响应中提到的,如果您希望避免在条件未满足时引发异常,则必须在接下来向函数添加一个额外的参数。

"functional" style:

next(filter(lambda i: i == 1000, range(100000000)), False)

"comprehension" style:

With this style you need to surround the comprehension expression with () to avoid a SyntaxError: Generator expression must be parenthesized if not sole argument:

使用这种样式,您需要用()包围理解表达式,以避免语法错误:如果不是唯一的参数,则必须插入生成器表达式:

next((i for i in range(100000000) if i == 1000), False)

#12


0  

In Python 3:

在Python 3:

a = (None, False, 0, 1)
assert next(filter(None, a)) == 1

In Python 2.6:

在Python 2.6:

a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1

#13


-1  

Oneliner:

Oneliner:

thefirst = [i for i in range(10) if i > 3][0]

If youre not sure that any element will be valid according to the criteria, you should enclose this with try/except since that [0] can raise an IndexError.

如果您不确定任何元素根据标准是否有效,您应该使用try/除了因为[0]会引发索引错误。

#1


281  

In Python 2.6 or better:

在Python 2.6或更好的版本中:

If you want StopIteration to be raised if no matching element is found:

如果您希望在没有找到匹配元素的情况下引发StopIteration:

next(x for x in the_iterable if x > 3)

接下来(x在the_iterable中,如果x > 3)

If you want default_value (e.g. None) to be returned instead:

如果您想要返回default_value(例如None):

next( (x for x in the_iterable if x>3), default_value)

接下来(x表示the_iterable中的x,如果x>3), default_value)

Note that you need an extra pair of parentheses around the generator expression in this case - they are needed always when the generator expression isn't the only argument.

请注意,在这种情况下,您需要在生成器表达式周围附加一对圆括号——当生成器表达式不是惟一的参数时,总是需要它们。

I see most answers resolutely ignore the next built-in and so I assume that for some mysterious reason they're 100% focused on versions 2.5 and older -- without mentioning the Python-version issue (but then I don't see that mention in the answers that do mention the next built-in, which is why I thought it necessary to provide an answer myself -- at least the "correct version" issue gets on record this way;-).

我看到大多数答案坚决忽略下一个内置的某些神秘的原因,所以我认为他们100%集中在版本2.5及以上,没有提及python版本问题(但我不看到答案,提提下一个内置的,这就是为什么我认为有必要提供答案,至少“正确版本”问题得到记录这样;-)。

In 2.5, the .next() method of iterators immediately raises StopIteration if the iterator immediately finishes -- i.e., for your use case, if no item in the iterable satisfies the condition. If you don't care (i.e., you know there must be at least one satisfactory item) then just use .next() (best on a genexp, line for the next built-in in Python 2.6 and better).

在2.5中,如果迭代器立即结束,迭代器的.next()方法会立即引发StopIteration——即。,对于您的用例,如果iterable中没有项满足条件。如果你不在乎(例如:,您知道必须至少有一个满意的项)然后使用.next()(在genexp上最好,Python 2.6中的下一个内置行更好)。

If you do care, wrapping things in a function as you had first indicated in your Q seems best, and while the function implementation you proposed is just fine, you could alternatively use itertools, a for...: break loop, or a genexp, or a try/except StopIteration as the function's body, as various answers suggested. There's not much added value in any of these alternatives so I'd go for the starkly-simple version you first proposed.

如果您确实关心这个问题,那么按照您在Q中第一次指出的那样将事情包装到函数中似乎是最好的,并且尽管您建议的函数实现很好,但是您也可以使用itertools, a for…:中断循环,或genexp,或try/except StopIteration,如各种答案所示。这些方案中没有多少附加价值,所以我将采用您最初提出的非常简单的版本。

#2


17  

As a reusable, documented and tested function

def first(iterable, condition = lambda x: True):
    """
    Returns the first item in the `iterable` that
    satisfies the `condition`.

    If the condition is not given, returns the first item of
    the iterable.

    Raises `StopIteration` if no item satysfing the condition is found.

    >>> first( (1,2,3), condition=lambda x: x % 2 == 0)
    2
    >>> first(range(3, 100))
    3
    >>> first( () )
    Traceback (most recent call last):
    ...
    StopIteration
    """

    return next(x for x in iterable if condition(x))

#3


13  

Similar to using ifilter, you could use a generator expression:

类似于使用ifilter,您可以使用生成器表达式:

>>> (x for x in xrange(10) if x > 5).next()
6

In either case, you probably want to catch StopIteration though, in case no elements satisfy your condition.

无论哪种情况,您都可能希望捕获StopIteration,以防没有元素满足您的条件。

Technically speaking, I suppose you could do something like this:

从技术上讲,我想你可以做这样的事情:

>>> foo = None
>>> for foo in (x for x in xrange(10) if x > 5): break
... 
>>> foo
6

It would avoid having to make a try/except block. But that seems kind of obscure and abusive to the syntax.

它将避免不得不尝试[除了]块。但这似乎有点模糊和滥用语法。

#4


8  

Damn Exceptions!

I love this answer. However, since next() raise a StopIteration exception when there are no items, i would use the following snippet to avoid an exception:

我喜欢这个答案。但是,由于next()在没有项目时引发了StopIteration异常,我将使用以下代码片段来避免异常:

a = []
item = next((x for x in a), None)

For example,

例如,

a = []
item = next(x for x in a)

Will raise a StopIteration exception;

将引发StopIteration异常;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

#5


6  

For older versions of Python where the next built-in doesn't exist:

对于不存在下一个内置程序的Python的旧版本:

(x for x in range(10) if x > 3).next()

#6


6  

I would write this

我想写这

next(x for x in xrange(10) if x > 3)

#7


5  

The itertools module contains a filter function for iterators. The first element of the filtered iterator can be obtained by calling next() on it:

itertools模块包含迭代器的筛选函数。过滤迭代器的第一个元素可以通过调用next()来获得:

from itertools import ifilter

print ifilter((lambda i: i > 3), range(10)).next()

#8


4  

By using

通过使用

(index for index, value in enumerate(the_iterable) if condition(value))

one can check the condition of the value of the first item in the_iterable, and obtain its index without the need to evaluate all of the items in the_iterable.

可以检查the_iterable中第一个项的值的条件,并获得它的索引,而不需要计算the_iterable中的所有项。

The complete expression to use is

要使用的完整表达式是

first_index = next(index for index, value in enumerate(the_iterable) if condition(value))

Here first_index assumes the value of the first value identified in the expression discussed above.

这里first_index假设在上面讨论的表达式中识别的第一个值的值。

#9


1  

Since you've requested a built-in one-liner, this will avoid the issue of a StopIteration exception, though it requires that your iterable is small so you can cast it to a list, since that is the only construct I know of which will swallow a StopIteration and let you peek at the values:

既然你要求一个内置的一行程序,这将避免抛出StopIteration异常的问题,尽管它要求你iterable很小所以你可以丢给一个列表,因为这是我所知道的唯一的构造将吞下抛出StopIteration和让你偷看的值:

(lambda x:x[0] if x else None)(list(y for y in ITERABLE if CONDITION))

(If no element matches, you will get None rather than a StopIteration exception.)

(如果没有元素匹配,您将得到None,而不是StopIteration异常。)

#10


1  

This question already has great answers. I'm only adding my two cents because I landed here trying to find a solution to my own problem, which is very similar to the OP.

这个问题已经有了很好的答案。我只是增加了我的2分,因为我来到这里是想找到一个解决我自己问题的方法,这和OP非常相似。

If you want to find the INDEX of the first item matching a criteria using generators, you can simply do:

如果您想使用生成器查找与标准匹配的第一个项目的索引,您只需:

next(index for index, value in enumerate(iterable) if condition)

#11


1  

The most efficient way in Python 3 are one of the following (using a similar example):

Python 3中最有效的方法是以下方法之一(使用类似的示例):

With "comprehension" style:

next(i for i in range(100000000) if i == 1000)

WARNING: The expression works also with Python 2, but in the example is used range that returns an iterable object in Python 3 instead of a list like Python 2 (if you want to construct an iterable in Python 2 use xrange instead).

警告:表达式也适用于Python 2,但是在示例中使用range返回Python 3中的可迭代对象,而不是像Python 2那样的列表(如果您想在Python 2中构造一个可迭代对象,请使用xrange)。

Note that the expression avoid to construct a list in the comprehension expression next([i for ...]), that would cause to create a list with all the elements before filter the elements, and would cause to process the entire options, instead of stop the iteration once i == 1000.

注意,表达式避免在下一个([i for…])中构造一个列表,这将导致在筛选元素之前创建一个包含所有元素的列表,并导致处理整个选项,而不是在i = 1000时停止迭代。

With "functional" style:

next(filter(lambda i: i == 1000, range(100000000)))

WARNING: This doesn't work in Python 2, even replacing range with xrange due that filter create a list instead of a iterator (inefficient), and the next function only works with iterators.

警告:这在Python 2中不起作用,甚至替换了xrange,因为该过滤器创建了一个列表而不是迭代器(效率低),下一个函数只与迭代器一起工作。

Default value

As mentioned in other responses, you must add a extra-parameter to the function next if you want to avoid an exception raised when the condition is not fulfilled.

正如在其他响应中提到的,如果您希望避免在条件未满足时引发异常,则必须在接下来向函数添加一个额外的参数。

"functional" style:

next(filter(lambda i: i == 1000, range(100000000)), False)

"comprehension" style:

With this style you need to surround the comprehension expression with () to avoid a SyntaxError: Generator expression must be parenthesized if not sole argument:

使用这种样式,您需要用()包围理解表达式,以避免语法错误:如果不是唯一的参数,则必须插入生成器表达式:

next((i for i in range(100000000) if i == 1000), False)

#12


0  

In Python 3:

在Python 3:

a = (None, False, 0, 1)
assert next(filter(None, a)) == 1

In Python 2.6:

在Python 2.6:

a = (None, False, 0, 1)
assert next(iter(filter(None, a))) == 1

#13


-1  

Oneliner:

Oneliner:

thefirst = [i for i in range(10) if i > 3][0]

If youre not sure that any element will be valid according to the criteria, you should enclose this with try/except since that [0] can raise an IndexError.

如果您不确定任何元素根据标准是否有效,您应该使用try/除了因为[0]会引发索引错误。