Map and filter are two built-in functions that make it easier to modifies iterables. These two functions are great for people working with data.
Map applies a function to all values in one or more iterables and filter runs each value through a function and creates an iterable with all truthy values.
map function
Let’s create a few lists and we want a function that takes the two lists and returns a new list where each element is the sum of the corresponding elements from the input lists:
a_list = list(range(10))
b_list = list(range(10, 30))
def add(value1, value2):
return value1 + value2Many beginner programmers would approach the problem of adding the values in these two lists together would be via a for loop (note that the two lists have different length and the iterable that zip creates will always return the values up to the shortest iterables):
added = []
for value1, value2 in zip(a_list, b_list):
added.append(add(value1, value2))Alternatively, we can use list comprehension:
added_comp = [add(value1, value2) for value1, value2 in zip(a_list, b_list)]Actually, map function is able to make this easier:
added_map = map(add, a_list, b_list)mapfunction has the samezipfunctionality of stopping the iteration as soon as he reaches the end of the shortest list.- obviosuly, if you’re passing multiple iterables, the function that you’re passing them into must be able to accept the same number of arguments (
addfunction accepts two arguments, so we give two iterables int the map function).
Let’s see all the results:
>>> added
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
>>> added_comp
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]
>>> added_map
<map object at 0x103246050>added_map is different from the other ones because the map function returns an iterator so we loop through each value in map to get it out:
for value in added_map:
print(value)Output:
10
12
14
16
18
20
22
24
26
28
A more preferable and concise way:
>>> added_map = map(add, a_list, b_list)
>>> list(added_map)
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]Note that we needed to recreate the added_map because the iterator is consumed after the for loop. Once we’ve iterated through all its elements, the iterator is exhausted. Therefore, if you try to convert it to a list with list(added_map) without recreating it, you’ll get an empty list since there’s nothing left to iterate over.
We can optimize even further with lambda function that allows us to define the function that we want in the map call signature. With this method we insert into a just line all the functionality we need:
>>> added_lambda = list(map(lambda x, y: x + y, a_list, b_list))
>>> added_lambda
[10, 12, 14, 16, 18, 20, 22, 24, 26, 28]filter function
This function runs through each value in an iterable and creates an iterator containing only the values of the iterable that is truthy wehn run through the function. The function we’ll use is:
def div_by_3(value):
return not value % 3Let’s complete this task with a for loop:
by_3 = []
for value in a_list:
if div_by_3(value):
by_3.append(value)Then
>>> by_3
[0, 3, 6, 9] Now, let’s do the same in a filter version:
>>> by_3_filter = list(filter(div_by_3, a_list))
>>> by_3_filter
[0, 3, 6, 9]Note that the filter function returns an iterator, so we need to use the list function to convert it into a list.
We reduced the code length, but we can reduce even more with a lambda:
>>> by_3_lambda = list(filter(lambda value: not value % 3, a_list))
>>> by_3_lambda
[0, 3, 6, 9]To get a better understanding of how this is working, let’s use map to see our boolean values for this list:
>>> by_3_map = list(map(div_by_3, a_list))
>>> by_3_map
[True, False, False, True, False, False, True, False, False, True]map and filter together
Let’s create a new list and a new function. We wanto this function to be flexible because we want to handle cases where you’re only passing in either an x value, an x, y value as well as x, y, z value (so y and z are None by default):
c_list = list(range(30, 60))
def to_point(x, y=None, z=None):
_x = f"x: {x}"
_y = f"y: {y}" if y else None
_z = f"z: {z}" if z else None
return ", ".join(filter(None, [_x, _y, _z]))We used a trick for the return statement because we want to have our x, y, and z values separated by a comma and a space. We’ll use the join function with a comma and a space, but we don’t want to add an extra comma when there isn’t a value that goes there and also join doesn’t reallt like if you pass in a value that is not a string, which could be the case for _y and _z if those values aren’t passed in. So, we can use filter to decide which values are being passed into join: the filter(None, [_x, _y, _z]) filters out any None values from the list [_x, _y, _z] because None checks the thruty values, so if you filter with None as function, its going to do just a simply thruty check. Then, ", ".join(...) combines the remaining non-None values into a single string, separated by commas.
To demonstrate that we’re going to use map:
x_points = list(map(to_point, a_list))
xy_points = list(map(to_point, a_list, b_list))
xyz_points = list(map(to_point, a_list, b_list, c_list))Then:
>>> x_points
['x: 0',
'x: 1',
'x: 2',
'x: 3',
'x: 4',
'x: 5',
'x: 6',
'x: 7',
'x: 8',
'x: 9']
>>> xy_points
['x: 0, y: 10',
'x: 1, y: 11',
'x: 2, y: 12',
'x: 3, y: 13',
'x: 4, y: 14',
'x: 5, y: 15',
'x: 6, y: 16',
'x: 7, y: 17',
'x: 8, y: 18',
'x: 9, y: 19']
>>> xyz_points
['x: 0, y: 10, z: 30',
'x: 1, y: 11, z: 31',
'x: 2, y: 12, z: 32',
'x: 3, y: 13, z: 33',
'x: 4, y: 14, z: 34',
'x: 5, y: 15, z: 35',
'x: 6, y: 16, z: 36',
'x: 7, y: 17, z: 37',
'x: 8, y: 18, z: 38',
'x: 9, y: 19, z: 39']So, basically we created a flexible system using both map and filter to combine three different lists into a formatted coordinate system.