Filtering a Collection
Filtering means picking out the elements of a collection that match a condition.
The condition takes one element and answers True (keep it) or False (skip it). A function that does this is called a predicate.
Filtering builds a new list. The original list is not changed.
In Python you filter with a list comprehension that has an if part: [w for w in words if len(w) > 3]. It reads almost like English: keep every w whose length is greater than 3.
Python also has a built-in filter() function that takes a predicate and a list. It works, but it returns a lazy iterator and needs a lambda for a simple condition, so the comprehension is usually clearer. This page uses comprehensions.
Filter by Predicate
The simplest way to filter is a comprehension whose if part holds the condition. Python walks through the list and keeps every element that passes the test.
Dictionaries work the same way: a dict comprehension over items() copies the matching key:value pairs into a new dictionary.
If the condition also needs the position of the element, enumerate gives you the index and the value together.
To keep the elements that do not match, put not in front of the condition. Python has no separate filter_not.
A list can hold values of different types. isinstance(v, str) checks whether a value is a string. A value can also be None, which means "no value"; compare with is not None to skip such entries.
Filter a list
Keep only the words that are longer than 3 letters. The condition len(w) > 3 sits in the if part of the comprehension.
Output: complete Python program
words = ["one", "two", "three", "four"]
# the if part keeps only the elements that pass the test
longer_than_3 = [w for w in words if len(w) > 3]
print(longer_than_3)
Filter a dictionary
Loop over items() and keep the pairs whose key ends with "1" and whose value is greater than 10. endswith() checks the end of a string.
Output: complete Python program
numbers_map = {"key1": 1, "key2": 2, "key3": 3, "key11": 11}
# a dict comprehension over items() filters key:value pairs
filtered_map = {k: v for k, v in numbers_map.items() if k.endswith("1") and v > 10}
print(filtered_map)
Filter using the index
enumerate gives the index and the value. Keep a word only when its index is not 0 and it has fewer than 5 letters.
Output: complete Python program
numbers = ["one", "two", "three", "four"]
# enumerate gives both the index and the value, so the condition can use both
filtered_idx = [w for i, w in enumerate(numbers) if i != 0 and len(w) < 5]
print(filtered_idx)
Filter with a negated condition
Drop the words with 3 letters or fewer. The condition len(w) = 3 is negated with not, so only the longer words stay.
Output: complete Python program
numbers = ["one", "two", "three", "four"]
# there is no filter_not: negate the condition with not
filtered_not = [w for w in numbers if not len(w) <= 3]
print(filtered_not)
Filter by type
The list holds None, a number, two strings and a float. isinstance(v, str) finds the strings. Each one is printed in upper case.
Output: complete Python program
numbers = [None, 1, "two", 3.0, "four"]
# isinstance(v, str) asks: is this value a string?
for v in numbers:
if isinstance(v, str):
print(v.upper())
Filter out None values
The list holds strings, but some entries are None. Keep the values that are not None with the is not None check.
Output: complete Python program
# None means "no value"
nullable_list = [None, "one", None, "two"]
values = [v for v in nullable_list if v is not None]
print(values)
Partition
Sometimes you want both groups: the elements that match and the ones that do not.
Python has no built-in partition for lists. A small helper does it: it loops once and appends each element to either the match list or the rest list. The helper returns both lists, because a Python function can return more than one value.
You could also write two comprehensions, one with the condition and one with its negation, but that walks the list twice; the helper does one pass.
Split a list in two
Words longer than 3 letters go into match; all the others go into rest. Both lists are printed.
Output: complete Python program
def partition(items, test):
match, rest = [], []
for v in items:
if test(v):
match.append(v)
else:
rest.append(v)
return match, rest
numbers = ["one", "two", "three", "four"]
match, rest = partition(numbers, lambda w: len(w) > 3)
print(match)
print(rest)
Test Predicates
Sometimes you do not need the matching elements, only a yes or no answer. Python has built-in functions for this:
- any: does at least one element pass the test?
any()answers this. - none: does no element pass the test? Put
notin front ofany(). - all: does every element pass the test?
all()answers this.
Both take a series of True/False values, so you pass them a generator expression such as w.endswith("e") for w in numbers. any() stops as soon as it finds a match.
Without a condition, "any element at all?" is len(numbers) > 0 and "no element at all?" is len(numbers) == 0.
On an empty list, all() returns True. There is no element that fails the test, so the test is never broken. This is called vacuous truth.
any
Check whether at least one word ends with "e", then check whether the list has any element at all.
Output: complete Python program
numbers = ["one", "two", "three", "four"]
# any() is True if at least one element passes the test
print(any(w.endswith("e") for w in numbers))
# "any element at all?" is just a length check
print(len(numbers) > 0)
none
Check that no word ends with "a", then check whether the list is empty.
Output: complete Python program
numbers = ["one", "two", "three", "four"]
# "none" is the opposite of "any": no element passes the test
print(not any(w.endswith("a") for w in numbers))
# "no element at all?" is just a length check
print(len(numbers) == 0)
all
Check whether every word ends with "e". Then call all() on an empty list, which gives True.
Output: complete Python program
numbers = ["one", "two", "three", "four"]
empty = []
# all() is True only when every element passes the test
print(all(w.endswith("e") for w in numbers))
# an empty list has no element that fails, so all() is True
print(all(w.endswith("e") for w in empty))