Map, reduce and filter are three powerful functional programming concepts which are intended to replace procedural elements such as loops, if-statements. This article assumes you are using Python 3. If you are not, it might be a good time to switch.

Map

Map takes a function function and a collection as its arguments. Collection can be any iterable such as list, tuple, sets etc. Function can be a regular function or a lambda function. Map makes a new empty collection, runs the function on each item in the original collection, and inserts each return value into new collection. It then returns the new collection.

The following example uses map function to find the squares for a list of numbers. Note that the map function does not modifies 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 map is a generator object. So if you want to reuse the object or do operations like printing, 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 this functions responsibility to implement the logic to combine values and return the combined value.

Filter

Like the map and reduce, 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 filter to filter out 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 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 combined

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 map and output of map becomes input for reduce. This is another powerful feature of function programming. You can chain function if they are guaranteed to be immutable.

A More Detailed Example

The example program below uses 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)))

Posted on
Category: Python
Tags: Python, Map, Reduce, Filter, Functional Programming