Select one item from the collection: Python

I created a utility function to return the expected single element from a generator expression

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

Is this a good way to do this?

 def one(g): try: val = g.next() try: g.next() except StopIteration: return val else: raise Exception('Too many values') except StopIteration: raise Exception('No values') 
+4
source share
8 answers

A simpler solution is to use unpacking. This will already do everything you need, including checking that it contains exactly one element.

Single item:

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

Too many elements:

 >>> 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 
+20
source

Simple approach:

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

If you really need an error when there is more than one value, you need a function. The simplest one I can think of is ( EDITED 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') 
+3
source

Do you mean?

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

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

+2
source

For those who use or are interested in a third-party library, more_itertools implements such a tool using the built-in 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: too many values to unpack (expected 1) mit.one(name for name in () if name == "bob") # ValueError: not enough values to unpack (expected 1, got 0) 

See more_itertools docs for more details. basic source code is equivalent to the accepted answer.

+2
source

Explore 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 implements many iterator blocks based on constructs from the Haskell and SML programming languages. Each of them has been redesigned in a form suitable for Python.

The module standardizes a basic set of fast, memory-efficient tools that are useful on their own or in combination. Standardization helps to avoid the problems with readability and reliability that arise when many different people create their own slightly different implementations, each of which has its own quirks and naming conventions.

Tools are designed to quickly coincide with each other. This makes it easier to create more specialized tools succinctly and efficiently in pure Python.

+1
source

Here is my attempt to use the one() function. I would avoid explicitly calling .next() and instead use a for loop.

 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 
+1
source

First, (to answer the real question!) Your solution will work just fine, as well as other options.

I would add that in this case, IMO generators are too complex. If you expect to have one value, you probably will never have enough memory to be a problem, so I would just use the 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] 
+1
source

How about using Python for .. in the syntax using a counter? Like the unknown 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 
0
source

All Articles