Map, reduce and filter are three powerful functional programming concepts. These can be used to replace procedural elements such as loops, if-statements. This article assumes you are using Python 3. If you are still using, it might be a good time to switch.
Map
The map takes a function and a collection as its arguments. The collection can be any iterable such as list, tuple, sets etc. The function can be a regular function or a lambda function. The map makes a new empty collection, runs the function on each item in the original collection, and inserts each return value into a new collection. It then returns the new collection.
The following example uses a map function to find the squares of a list of numbers. Note that the map function does not modify the original collection. This is a very important functional programming property, called immutability.
squares = map(lambda num: num**2, range(10))
print(type(squares)) # Outputs <class 'map'>
squares_list = list(squares)
print(squares_list) # Outputs [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
The return value of a map is a generator object. So if you want to reuse the object or do operations like printing to the screen, it is better to convert the generator object to a regular collection, such as a list.
Reduce
Reduce takes a function and a collection of objects as arguments and returns a value that is created by combining the items in the collection. There is also a third optional argument, which acts as the starting value when combining items. If no starting value is provided, the first element in the collection is used as the starting value.
from functools import reduce
numbers_sum = reduce(lambda acc, num: acc + num, range(1, 11))
print(numbers_sum) # Outputs 55
Unlike map, the reduce function is not available in interpreter scope unless you import it from functools. The function you give to reduce should take two arguments: the first argument is the combined sum up to the current step, and the second argument is the value to be added in the current step. It is the responsibility of this function to implement the logic to combine values and return the combined value.
Filter
Like the map and reduce, the filter also takes two arguments, a function and a collection of objects. It then returns a collection of every item for which the function returned True.
The example below demonstrates using a filter to filter out the odd number from a list of numbers.
even_numbers = filter(lambda x: x % 2 == 0, range(10))
print(type(even_numbers)) # Outputs <class 'filter'>
even_numbers_list = list(even_numbers)
print(even_numbers_list) # Outputs [0, 2, 4, 6, 8]
The return value of the filter is also a generator object. So if you want to do repeated operations on it, it is better to convert it into a regular iterable like list or tuple.
Combining Map, Reduce and Filter
Map, reduce and filter becomes even more powerful when they are combined. The example below uses a combination of these functions to find the sum of squares of all even numbers from a list of numbers.
from functools import reduce
even_numbers = filter(lambda x: x % 2 == 0, range(10))
squares = map(lambda num: num**2, even_numbers)
squares_sum = reduce(lambda acc, num: acc + num, squares)
print(squares_sum) # Outputs 120
Alternatively, you can combine map, reduce and filter into a single step.
from functools import reduce
squares_sum = reduce(lambda acc, num: acc + num, map(lambda num: num**2, filter(lambda x: x % 2 == 0, range(10))))
print(squares_sum) # Outputs 120
The output of filter becomes the input for the map and output of the map becomes the input for reduce. This is another powerful feature of functional programming. You can chain function if they are guaranteed to be immutable.
A More Detailed Example
The example program below uses the map, reduce and filter to do some calculations of a student data set. The code is self-explanatory.
from functools import reduce
students = [
{
'id': 1,
'name': 'Arya Stark',
'score': 78,
},
{
'id': 2,
'name': 'Jon Snow',
'score': 85
},
{
'id': 3,
'name': 'Daenerys Targaryen',
'score': 90,
}
]
print("\n\nStudent data")
for student in students:
print(student)
# Map
def find_gpa(student):
student['gpa'] = student['score'] / 10
return student
students_gpa = map(find_gpa, students)
students_gpa = list(students_gpa)
print("\n\n\nStudent data with GPA")
for student_gpa in students_gpa:
print(student_gpa)
# Filter
def is_top_student(student):
return student['gpa'] > 8
top_students = filter(is_top_student, list(students_gpa))
top_students = list(top_students)
print("\n\n\nTop students")
for top_student in top_students:
print(top_student)
# Reduce
def gpa_adder(acc, student):
return acc + student['gpa']
gpa_sum = reduce(gpa_adder, top_students, 0)
print("\n\n\nSum: {}".format(gpa_sum))
print("\nAverage GPA: {}".format(gpa_sum/len(top_students)))