Reputation: 27211
I've got a list, which may or may not contain a unique element satisfying a given predicate. I am looking for an expression which evaluates to an item satisfying that predicate if it exists and is unique, and otherwise returns None
. Something like
numbers = [4, 3, 9, 7, 1, 2, 8]
print(the(item for item in numbers if item > 10)) # None
print(the(item for item in numbers if item % 2 == 0)) # None
print(the(item for item in numbers if item % 7 == 0)) # 7
Is there a built-in idiom for this, or do I have to write my own the
function?
Upvotes: 2
Views: 376
Reputation: 28606
You could try asking for two elements with islice
, would be a bit simpler:
def the(it):
tmp = list(islice(it, 2))
return tmp[0] if len(tmp) == 1 else None
Or a loop:
def the(it):
value = None
for i, value in enumerate(it):
if i == 1:
return None
return value
Or yet another way to use next
:
def the(it):
first = next(it, None)
o = object()
if next(it, o) is o:
return first
Or similar to yours:
def the(it):
first = next(it, None)
try:
next(it)
except:
return first
Upvotes: 1
Reputation: 78556
You can use the following lazy approach. Get the next two items from the generator expression and return None
if the second item from the generator is not None
:
def func(lst, pred):
gen = (i for i in lst if pred(i))
v, w = next(gen, None), next(gen, None)
return v if w is None else None
print(func([4, 3, 9, 7, 1, 2, 8], lambda x: x>10))
# None
print(func([4, 3, 9, 7, 1, 2, 8], lambda x: x % 2 == 0))
# None
print(func([4, 3, 9, 7, 1, 2, 8], lambda x: x % 7 == 0))
# 7
print(func([4, 3, 9, 0, 1, 2, 8], lambda x: x % 7 == 0))
# 0
Upvotes: 0
Reputation: 12341
This should be a simple solution for this problem:
def the(items):
return items[0] if (len(items) == 1) else None
numbers = [4, 3, 9, 7, 1, 2, 8]
print(the([item for item in numbers if item > 10])) # None
print(the([item for item in numbers if item % 2 == 0])) # None
print(the([item for item in numbers if item % 7 == 0])) # 7
You could also apply the following format. It might not be any simpler in terms of code, but it certainly is faster when the item count is large.
print(the(list(filter(lambda x: x > 10, numbers)))) # None
print(the(list(filter(lambda x: x % 2 == 0, numbers)))) # None
print(the(list(filter(lambda x: x % 7 == 0, numbers)))) # 7
Upvotes: 0
Reputation: 148940
I do not know a single expression way for that, but that simple function should work:
def the(it, cond):
l = [ i for i in it if cond(i) ]
return l[0] if len(l) == 1 else None
Test:
>>> print(the(numbers,(lambda x: x > 10)))
None
>>> print(the(numbers,(lambda x: x % 7 == 0)))
7
>>> print(the(numbers,(lambda x: x % 2 == 0)))
None
Upvotes: 4
Reputation: 27211
For the record, I can just write the
as
def the(it):
it = iter(it)
try:
value = next(it)
except StopIteration:
return None
try:
next(it)
except StopIteration:
return value
return None
Upvotes: 0