There's the last guy called
reduce but it's not here because it's like the odd guy out and requires a dedicated article to it
Before we map, let us take a look at a beautiful for-loop in Python.
squares =  for i in range (2, 10): squares.append(i ** 2) print(squares) # [4, 9, 16, 25, 36, 49, 64, 89]
The point of the above is to take the numbers 2 up to 10 (exclusive) and get the squares of them. With a
map function, we can easily achieve that with grace and mercy.
The map function, included in the global namespace by default is an elegant way to perform operations on an iterable, basically replacing an expressive for-loop, like so:
# returns an iterator of our results squares = map(lambda x: x ** 2, range(2, 10)) # we can then get a list with squares = list(squares)
map function accepts two arguments: a function and an iterable; and applies the function to each member of the iterable and spits the result as a generator. Generally, lambda functions are preferred as the function to be used due to its conciseness. We can with this define the internal implementation of a
map function crudely to be:
from typing import ( Any, Callable, Iterable, Generator ) def map(func: Callable, iterable: Iterable) -> Generator[Any, Any, Any]: # the first `Any` is the type of the value being returned # we can replace the `Generator` with an `Iterator` # but we're not here to learn about types for member in iterable: yield func(member) # so we call it like so: map(func, iterable)
With this information,
map can (not every time) essentially replace your for-loops with a more concise definition that looks linear to the eye especially when you are performing simple operations. We can see more examples of
map like so:
# This is only for the purpose of demonstration names = list(map( lambda x: (input("Enter a name: ")).strip(), range(10) )) # displays names print(names)
Performance with the usual
You may be concerned as to the performance implications of using a map and then wrapping the results in a list. Relieving your fears is the fact that
map returns a generator-like (an iterator) result which ensures results are yielded only on demand. So wrapping the result in a list would have little or no performance overhead.
Filter on the other hand is a function with a similar structure with
map except that it executes the function against the iterable and only returns value that evaluates to
True. In this sense, it:
Filters out falsities and untrue things, revealing their true nature in an elegant display of absence.
A good way to use
filter is a to filter out odd numbers from a list:
even_numbers = list(filter( lambda x: not (x % 2), range(100) )) print(even_numbers) # -> 0, 2, 4, 6, 8, 10, ..., 98
The inner working of
filter can be described crudely as:
from typing import ( Any, Callable, Iterable, Generator, Optional ) def filter(func: Optional[Callable], iterable: Iterable) -> Generator[Any, Any, Any]: for member in iterable: result = func(member) if result: yield result # so we call it like so: filter(func, iterable)
You'll notice that the
func attribute is marked as optional. This is because you can pass
filter, except that you'll get only values in the iterable that evaluate as
And so therefore
Go out there and populate codebases like JS spawns till you get the kind of comment I got on a PR:
A list comprehension is better than all these
mapyou're putting everywhere.
If you're fascinated by lambda functions, those
lambda x: x ** 2 you see in the
map functions, you can read my article on lambda functions.