Working with filters

Working with filters

A closer look at the rule and the results.

The rule is a function.

A filter’s rule is called a predicate. It takes one item and usually answers True or False.

Give it a name

def keep(x):
    return x > 2
filter(keep, numbers)

Or write it inline

lambda x: x > 2
filter(lambda x: x > 2, numbers)

lambda creates a small function. x is one item. The expression after : supplies the answer.

From [1, 2, 3, 4, 5, 6]
Both keep[3, 4, 5, 6]

The comparison is True for 3, 4, 5, and 6.

Pass keep, not keep(). The filter calls the rule for each item.

Can the items come from something other than a list?

Yes. An iterable supplies items one at a time. Lists, tuples, ranges, and strings all work. A string supplies individual characters:

list(filter(str.isupper, "HeLLo"))
# ["H", "L", "L"]

str.isupper checks whether a character is uppercase. list() still makes a list; use "".join(filter(str.isupper, "HeLLo")) to get "HLL". Dictionaries supply keys by default.

Results arrive when you ask.

filter() creates an iterator: it remembers its position and gives you results one at a time. Creating it does not check the items yet. That’s called lazy evaluation.

numbers = [1, 2, 3, 4, 5]f = filter(lambda x: x > 2, numbers)
Items in the source0 of 5 checked
  1. 1Not read
  2. 2Not read
  3. 3Not read
  4. 4Not read
  5. 5Not read
Filter createdReady

No checks yet. Ask for the first match with next(f).

Already returned: []

One request can check several items.
next(f) skips nonmatches until it finds one match.

Each result is used once.
list(f) collects only what is left, not what you already read.

The source list stays unchanged. “New filter” creates a fresh iterator over that list. “Already returned” is a visual record for this example; the filter itself does not save past results.

What happens when there are no results left?

list(f) returns []. next(f) raises StopIteration, Python’s signal that the iterator is finished. Use next(f, None) if you want a default value instead.

Save matches = list(f) once if you need to use the matches repeatedly. The saved list is reusable; the filter is consumed.

“True” can mean more than True.

Python treats values as truthy or falsy, just as it does in an if statement. A filter keeps an item when the rule’s answer is truthy.

Falsy values include 0, False, None, and empty text or collections.

list(filter(None, values))

values · The original items, in order

0Skip
1Keep
""Skip
"hello"Keep
NoneSkip
FalseSkip
5Keep
Result[1, "hello", 5]

None in the rule’s place means “test each item itself.” It removes every falsy item, not just None.

What if my rule returns a number?

Python tests the answer’s truth value. With lambda x: x - 2, zero is falsy; both negative and positive nonzero numbers are truthy.

ItemRule’s answerDecision
1-1Keep 1
20Skip 2
31Keep 3

The result is [1, 3]: the original items, not the rule’s answers.

When would I use a comprehension or map?

Use a list comprehension for a simple selection you want as a list immediately. filter() lets you request matches as you need them. map() returns the function’s answers for every item.

Filter: select itemslist(filter(lambda x: x > 2, [1, 2, 3]))→ [3]
Comprehension: build a list[x for x in [1, 2, 3] if x > 2]→ [3]
Map: return each answerlist(map(lambda x: x > 2, [1, 2, 3]))→ [False, False, True]

Put the details together.

Predict what stays and what is left to read.

Try the practice