Get the first item from an iterable that matches a

2018-12-31 13:13发布

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条回答
姐姐魅力值爆表
2楼-- · 2018-12-31 13:59

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

>>> (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.

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.

查看更多
余生无你
3楼-- · 2018-12-31 14:01

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:

(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.)

查看更多
查无此人
4楼-- · 2018-12-31 14:03

In Python 3:

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

In Python 2.6:

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

EDIT: I thought it was obvious, but apparently not: instead of None you can pass a function (or a lambda) with a check for the condition:

a = [2,3,4,5,6,7,8]
assert next(filter(lambda x: x%2, a)) == 3
查看更多
不再属于我。
5楼-- · 2018-12-31 14:11

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.

查看更多
倾城一夜雪
6楼-- · 2018-12-31 14:11

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:

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

For example,

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

Will raise a StopIteration exception;

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
查看更多
只若初见
7楼-- · 2018-12-31 14:13

I would write this

next(x for x in xrange(10) if x > 3)
查看更多
登录 后发表回答