Python One-Liner: Getting Only the First Match in a List Comprehension

Python’s list comprehensions are great, but I’ve found a new (to me) use of them: iterating over a list and returning the first match when there might be multiple possible matches. (To be more accurate, my solution uses a generator expression rather than a list comprehension)

In other words, I’m emulating a break statement in the loop, but only for the first match. In code, that is:

1
2
3
4
5
val = None
for x in some_list:
    if match(x):
        val = x
        break

I could do this with a list comprension and getting the element at index 0:

1
val = [x for x in some_list if match(x)][0]

…but that means the whole list is created, and what if the list is large and/or the match() function is expensive? I’d really like to just stop looping when I’ve got a match. Generator expressions come in handy here:

1
val = (x for x in some_list if match(x)).next()

Also note that in Python3, functions like filter() return an iterator (which may have a generator under the hood, I’m not sure), so this is possible:

1
val = next(filter(match, some_list))

Now I’ve got the first matching value, and I don’t have to match against every item in the list. Hooray for functional Python.

Update:
As pointed out by Emanuel Hoogeveen in the comments, my solution above only works if there is a match, otherwise it raises a StopIteration exception. The following provides a default value if there is no match (from StackOverflow via Emanuel):

1
val = next((x for x in some_list if match(x)), None)
This entry was posted in Programming and tagged , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

4 Comments

  1. AP
    Posted 2013.04.01 at 14:55 | Permalink

    Hi– This isn’t a comment on your post, I’m afraid, but rather a means of getting your attention since a preferred method of contacting you isn’t listed on your page (and I don’t want to get too intrusive). So: I’m no one special, but I have just been accepted to the computational linguistics master’s program at UW and, in an online search for current students there, I found your page — and I’d like to ask you a few questions about your experience there, if it doesn’t bother you! If you’re keen, please email me. If not, I am sorry to have bothered you!

  2. goodmami
    Posted 2013.04.01 at 21:45 | Permalink

    No I don’t mind, but next time you can email me directly instead of posting a comment to an unrelated blog post ;) My email address is available in at least 2 places on my website. But you’re right, maybe it isn’t obvious enough.

    Expect an email soon.

  3. Emanuel Hoogeveen
    Posted 2013.07.05 at 15:27 | Permalink
    1
    val = (x for x in some_list if match(x)).next()

    only works if some_list contains at least one match – if no matches are found, it will raise a StopIteration exception! To prevent this, use the optional second argument of the builtin next() function:

    1
    val = next((x for x in some_list if match(x)), None)

    Posting this since Google took me here first – I found the answer here:
    http://stackoverflow.com/questions/7102050/how-can-i-get-a-python-generator-to-return-none-rather-than-stopiteration

  4. goodmami
    Posted 2013.07.05 at 15:43 | Permalink

    An unfortunate oversight on my part. Thanks for the suggestion! I’ve updated the post.

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>