dumbass
dumbass

Reputation: 27211

Python idiom to return the only element or None

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

Answers (5)

Stefan Pochmann
Stefan Pochmann

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

Moses Koledoye
Moses Koledoye

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

Ébe Isaac
Ébe Isaac

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

Serge Ballesta
Serge Ballesta

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

dumbass
dumbass

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

Related Questions