Why is there no first(iterable) built-in function in Python?

PythonIteratorGenerator

Python Problem Overview


I'm wondering if there's a reason that there's no first(iterable) in the Python built-in functions, somewhat similar to any(iterable) and all(iterable) (it may be tucked in a stdlib module somewhere, but I don't see it in itertools). first would perform a short-circuit generator evaluation so that unnecessary (and a potentially infinite number of) operations can be avoided; i.e.

def identity(item):
    return item

def first(iterable, predicate=identity):
    for item in iterable:
        if predicate(item):
            return item
    raise ValueError('No satisfactory value found')

This way you can express things like:

denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
    if all(i % denominators == 0 for denominator in denominators))

Clearly you can't do list(generator)[0] in that case, since the generator doesn't terminate.

Or if you have a bunch of regexes to match against (useful when they all have the same groupdict interface):

match = first(regex.match(big_text) for regex in regexes)

You save a lot of unnecessary processing by avoiding list(generator)[0] and short-circuiting on a positive match.

Python Solutions


Solution 1 - Python

In Python 2, if you have an iterator, you can just call its next method. Something like:

>>> (5*x for x in xrange(2,4)).next()
10

In Python 3, you can use the next built-in with an iterator:

>>> next(5*x for x in range(2,4))
10

Solution 2 - Python

There's a Pypi package called “first” that does this:

>>> from first import first
>>> first([0, None, False, [], (), 42])
42

Here's how you would use to return the first odd number, for example:

>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1)
7

If you just want to return the first element from the iterator regardless of whether is true or not, do this:

>>> first([0, None, False, [], (), 42], key=lambda x: True)
0

It's a very small package: it only contains this function, it has no dependencies, and it works on Python 2 and 3. It's a single file, so you don't even have to install it to use it.

In fact, here's almost the entire source code (from version 2.0.1, by Hynek Schlawack, released under the MIT licence):

def first(iterable, default=None, key=None):
    if key is None:
        for el in iterable:
            if el:
                return el
    else:
        for el in iterable:
            if key(el):
                return el
    return default

Solution 3 - Python

I asked a similar question recently (it got marked as a duplicate of this question by now). My concern also was that I'd liked to use built-ins only to solve the problem of finding the first true value of a generator. My own solution then was this:

x = next((v for v in (f(x) for x in a) if v), False)

For the example of finding the first regexp match (not the first matching pattern!) this would look like this:

patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ]
text = 'abc'
firstMatch = next(
  (match for match in
    (re.match(pattern, text) for pattern in patterns)
   if match),
  False)

It does not evaluate the predicate twice (as you would have to do if just the pattern was returned) and it does not use hacks like locals in comprehensions.

But it has two generators nested where the logic would dictate to use just one. So a better solution would be nice.

Solution 4 - Python

There's some ambiguity in your question. Your definition of first and the regex example imply that there is a boolean test. But the denominators example explicitly has an if clause; so it's only a coincidence that each integer happens to be true.

It looks like the combination of next and itertools.ifilter will give you what you want.

match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))

Solution 5 - Python

There is a "slice" iterator in itertools. It emulates the slice operations that we're familiar with in python. What you're looking for is something similar to this:

myList = [0,1,2,3,4,5]
firstValue = myList[:1]

The equivalent using itertools for iterators:

from itertools import islice
def MyGenFunc():
    for i in range(5):
        yield i

mygen = MyGenFunc()
firstValue = islice(mygen, 0, 1)
print firstValue 

Solution 6 - Python

Haskell makes use of what you just described, as the function take (or as the partial function take 1, technically). Python Cookbook has generator-wrappers written that perform the same functionality as take, takeWhile, and drop in Haskell.

But as to why that's not a built-in, your guess is as good as mine.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncdlearyView Question on Stackoverflow
Solution 1 - PythonlioriView Answer on Stackoverflow
Solution 2 - PythonFlimmView Answer on Stackoverflow
Solution 3 - PythonAlfeView Answer on Stackoverflow
Solution 4 - PythonA. CoadyView Answer on Stackoverflow
Solution 5 - PythonZoran PavlovicView Answer on Stackoverflow
Solution 6 - PythonMark RushakoffView Answer on Stackoverflow