Welcome, guest | Sign In | My Account | Store | Cart

A common practice when dealing with sequences is to find the first or last item in the list that satisfies a predicate. This simple recipe increases the readability and writability for these tasks and nothing more.

Python, 42 lines
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
def first(seq, pred=None):
    """Return the first item in seq for which the predicate is true.
    
    If the predicate is None, return the first item regardless of value.

    If no items satisfy the predicate, return None.
    """
    if pred is None:
        pred = lambda x: True
    for item in seq:
        if pred(item):
            return item
    return None

def last(seq, pred=None):
    """Return the last item in seq for which the predicate is true.
    
    If the predicate is None, return the last item regardless of value.

    If no items satisfy the predicate, return None.
    """
    if pred is None:
        pred = lambda x: True
    for item in reversed(seq):
        if pred(item):
            return item
    return None

# Just get the first item :)
# >>> seq = 'abc'
# >>> first(seq)
# 'a'

# Get the first item greater than 10
# >>> seq = [1, 2, 4, 12, 13, 15, 17]
# >>> first(seq, lambda x: x > 10)
# 12

# Get the last non-None/False/empty item
# >>> seq = ["one", "two", "three", "", None]
# >>> last(seq, bool)
# 'three'

The goal if this recipe is to simply increase readability and writability of a common task. An alternative it to use list comprehensions:

[x for x in seq if x > 2][0] [x for x in seq if x > 2][-1]

However, this does unnecessary work by not short-circuiting, since we only want the first item. It also does not handle the case where no items satisfy the predicate.

2 comments

Ian Bicking 18 years, 9 months ago  # | flag

Use of None. Typically "None" in the place of a predicate means the identity function. For instance:

filter(None, lst)

Means to filter out all the items in the list that aren't true. So if the predicate is None it should be replaced with

lambda v: v

Or something like that.

Brian Beck (author) 18 years, 9 months ago  # | flag

True, I was debating whether or not to post the default as the identity function. Would it not be the same as the default being 'bool'?

My reasoning was that first(seq) should return the first item in the sequence no matter what, since that's what it 'says.'

Created by Brian Beck on Tue, 2 Aug 2005 (PSF)
Python recipes (4591)
Brian Beck's recipes (5)

Required Modules

  • (none specified)

Other Information and Tasks