[ACCEPTED]-select single item from a collection : Python-generator

Accepted answer
Score: 28

A simpler solution is to use tuple unpacking. This 3 will already do everything you want, including 2 checking that it contains exactly one item.

Single 1 item:

 >>> name, = (name for name in ('bob','fred') if name=='bob')
 >>> name
 'bob'

Too many items:

>>> name, = (name for name in ('bob','bob') if name=='bob')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: too many values to unpack

No items:

>>> name, = (name for name in ('fred','joe') if name=='bob')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: need more than 0 values to unpack
Score: 5

For those using or interested in a third-party 3 library, more_itertools implements such a tool with native 2 error handling:

> pip install more_itertools

Code

import more_itertools as mit


mit.one(name for name in ("bob", "fred") if name == "bob")
# 'bob'

mit.one(name for name in ("bob", "fred", "bob") if name == "bob")
# ValueError: ...

mit.one(name for name in () if name == "bob")
# ValueError: ...

See more_itertools docs for details. The underlying source code is 1 similar to the accepted answer.

Score: 4

Simple approach:

print (name for name in ('bob', 'fred') if name == 'bob').next()

If you really want an error 3 when there is more than one value, then 2 you need a function. The most simple I can 1 think of is (EDITED to work with lists too):

def one(iterable):
    it = iter(iterable)
    val = it.next()
    try:
        it.next()
    except StopIteration:
        return val
    else:
        raise Exception('More than one value')
Score: 1

Have a look into the itertools.islice() method.

>>> i2=itertools.islice((name for name in ('bob','fred') if name=='bob'),0,1,1)
>>> i2.next()
'bob'
>>> i2.next()
Traceback (most recent call last):
  File "<interactive input>", line 1, in <module>
StopIteration
>>> 

This module 15 implements a number of iterator building 14 blocks inspired by constructs from the Haskell 13 and SML programming languages. Each has 12 been recast in a form suitable for Python.

The 11 module standardizes a core set of fast, memory 10 efficient tools that are useful by themselves 9 or in combination. Standardization helps 8 avoid the readability and reliability problems 7 which arise when many different individuals 6 create their own slightly varying implementations, each 5 with their own quirks and naming conventions.

The 4 tools are designed to combine readily with 3 one another. This makes it easy to construct 2 more specialized tools succinctly and efficiently 1 in pure Python.

Score: 1

Do you mean?

def one( someGenerator ):
    if len(list(someGenerator)) != 1: raise Exception( "Not a Singleton" )

What are you trying to accomplish 1 with all the extra code?

Score: 1

Here is my try at the one() function. I would 2 avoid the explicit .next() call and use a for loop 1 instead.

def one(seq):
    counter = 0
    for elem in seq:
        result = elem
        counter += 1
        if counter > 1:
            break
    if counter == 0:
        raise Exception('No values')
    elif counter > 1:
        raise Exception('Too many values')
    return result
Score: 1

First, (to answer the actual question!) your 7 solution will work fine as will the other 6 variants proposed.

I would add that in this 5 case, IMO, generators are overly complicated. If 4 you expect to have one value, you'll probably 3 never have enough for memory usage to be 2 a concern, so I would have just used the 1 obvious and much clearer:

children = [name for name in ('bob','fred') if name=='bob']
if len(children) == 0:
    raise Exception('No values')
elif len(children) > 1:
    raise Exception('Too many values')
else:
    child = children[0]
Score: 0

How about using Python's for .. in syntax 1 with a counter? Similar to unbeknown's answer.

def one(items):
    count = 0
    value = None

    for item in items:
        if count:
            raise Exception('Too many values')

        count += 1
        value = item

    if not count:
        raise Exception('No values')

    return value

More Related questions