Python @ DjangoSpin

Python: Using builtin constructors map(), filter(), reduce()

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon
Reading Time: 4 minutes

map, filter, reduce in Python

map, filter, reduce in Python

map()

It is fairly common in programming to iterate over an iterable object, send each element to a function which returns a manipulated version of the element. While this can be done explicitly with a for loop, the builtin class in Python 3 called map allows for shorter and cleaner code to achieve this. The constructor of map class returns an iterator of the results of a function applied to every element of an iterable object. Example:

>>> help(map)
...
class map(object)
 |  map(func, *iterables) --> map object
 |  
 |  Make an iterator that computes the function using arguments from
 |  each of the iterables.  Stops when the shortest iterable is exhausted.
...
 
>>> def addFive(number):
	return number + 5

>>> map( addFive, [1, 2, 3, 4] )
<map object at 0x03A1EDD0>
>>> aMapObject = map( addFive, [1, 2, 3, 4] )
>>> for result in aMapObject:
	print(result)

	
6
7
8
9

The map class is designed to be used in conjunction with Lambda Expressions for tighter code. Lambda Expressions are used to create anonymous functions. In a nutshell, these functions do not have the 'def' keyword, instead have a 'lambda' keyword, take any number of arguments and return a single value in the form of an expression. Click here to know more about Lambda Expressions.

>>> aMapObject = map( lambda x: x + 5, [1, 2, 3, 4] )
>>> next(aMapObject)
6


>>> aMapObject = map( lambda x: x + 5, range(6) )
>>> next(aMapObject)
5

The constructor of the map class can actually take more than one iterable object, in the event that the function takes more than arguments.

>>> anotherMapObject = map( lambda x, y: x + y, [1, 2, 3, 4], [10, 20, 30, 40] )
>>> for element in anotherMapObject:
	print(element)

	
11
22
33
44

In the event that the length of the iterable objects is uneven, the population of the to-be-returned iterator stops as soon as the shortest out of the iterable objects is fully traversed.

>>> anotherMapObject = map( lambda x, y: x + y, [1, 2, 3, 4, 5, 6], [10, 20, 30, 40] )
>>> for element in anotherMapObject:
	print(element)

11
22
33
44 

You can cast the returned iterator into a list using the builtin list() function. However, as the documentation says, rather than converting an iterator into a list, it is better to just use a list comprehension especially when lambda expressions are being used to generate the resultant elements.

>>> a = list(map( lambda x: x + 5, [1, 2, 3, 4] ))
>>> a
[6, 7, 8, 9]

In Python 2, map is a builtin function instead of a class. Its usage is same as in Python 3, except for the fact that in Python 2, it returns a list instead of an iterator.


filter()

The builtin filter class does what you expect it to. It filters out the elements which do not match a criteria and creates an iterator out of the elements which match it. The constructor of the filter class accepts a function and an iterable object. The function either returns True or False for an input, and the input which gives a True value gets populated in the iterator returned by the filter class.

>>> help(filter)
...
class filter(object)
 |  filter(function or None, iterable) --> filter object
 |  
 |  Return an iterator yielding those items of iterable for which function(item)
 |  is true. If function is None, return the items that are true.
...

Say you wanted to filter out the multiples of 7 lying in the first 30 numbers. You can achieve this using a for loop as well, but the filter class makes for cleaner code.

>>> def multiplesOfSeven(number):
	return number % 7 == 0

>>> aFilterObject = filter( multiplesOfSeven, range(31) )
>>> for element in aFilterObject:
	print(element)

0
7
14
21
28




>>> aFilterObject = filter( lambda x: x % 7 == 0, range(30) )
>>> next(aFilterObject)
0
>>> next(aFilterObject)
7




>>> for element in filter( lambda x: x % 7 == 0, range(31) ):
	print(element)

0
7
14
21
28

If the function provided is None, it creates an iterator only out of true elements of the supplied iterable object i.e. elements which are not 0 and are not empty strings ( '' ).

>>> for number in range(5): print(number)

0
1
2
3
4

>>> aFilterObject = filter( None, range(5) )
>>> next(aFilterObject)
1									# Not 0

Here's another example where the filter class extracts words beginning with the letter 'a' out of a list of words.

>>> listOfWords = ['aardvark', 'apple', 'ball', 'cat']
>>> wordsBeginningWithA = filter( lambda x: x.startswith('a'), listOfWords )
>>> for word in wordsBeginningWithA:
	print(word)

aardvark
apple

The filter class, like map class, is designed to be used in conjunction with Lambda Expressions for tighter code. Lambda Expressions are used to create anonymous functions. In a nutshell, these functions do not have the def keyword, instead have a lambda keyword, take any number of arguments and return a single value in the form of an expression. Click here to know more about Lambda Expressions.

Like with the map class, you can cast the returned iterator into a list using the builtin list() function. However, as the documentation says, rather than converting an iterator into a list, it is better to just use a list comprehension especially when lambda expressions are being used to generate the resultant elements.

>>> multiplesOfSeven = list( filter( lambda x: x % 7 == 0, range(30) ) )
>>> multiplesOfSeven
[0, 7, 14, 21, 28]

In Python 2, filter is a builtin function instead of a class. Just like map, its usage is same as in Python 3, except for the fact that in Python 2, it returns a list instead of an iterator.


reduce()

There are times when you have a sequence of values and you want to reduce it to a single value. The reduce() function in module functools applies a function which operates on 2 elements at a time, to the elements of an iterable object, going from left to right.

>>> import functools
>>> help(functools.reduce)
Help on built-in function reduce in module _functools:

reduce(...)
    reduce(function, sequence[, initial]) -> value
    
    Apply a function of two arguments cumulatively to the items of a sequence,
    from left to right, so as to reduce the sequence to a single value.
    For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates
    ((((1+2)+3)+4)+5).  If initial is present, it is placed before the items
    of the sequence in the calculation, and serves as a default when the
    sequence is empty.

Say you have a list of numbers [1, 2, 3, 4, 5] and an anonymous function lambda x, y: x + y. It is a lambda expression which takes two arguments and returns their sum. The reduce() function functools.reduce(lambda x, y: x + y, [1, 2, 3, 4, 5]) will evaluate ((((1 + 2) + 3) + 4) + 5) and return a single value 15.

>>> import functools
>>> functools.reduce( lambda x, y: x + y, [1, 2, 3, 4, 5] )
15

>>> functools.reduce( lambda x, y: x + y, range(6) )
15

# OTHER EXAMPLES
# reducing a list of numbers into their product.
>>> def product(a, b):
	return a * b

>>> functools.reduce( product, [1, 2, 3, 4, 5] )
120


# reducing a bunch of strings into a resultant string
>>> def concatenate(a, b):
	return a + b

>>> functools.reduce(concatenate, ['Hello', ' ', 'there!'])
'Hello there!'



# finding the largest element in an iterable object
>>> functools.reduce( lambda x, y: x if ( x > y ) else y, [10,5,15, 22,14] )
22

In order to verify the order in which the elements of the iterable object are passed to the specified function, let's make our function verbose.

>>> import functools
>>> def addTwoNumbers(x, y):
	print("x = {}; y = {}; x + y = {}".format(x, y, x + y))
	return x + y

>>> functools.reduce( addTwoNumbers, [1, 2, 3, 4, 5] )
x = 1; y = 2; x + y = 3
x = 3; y = 3; x + y = 6
x = 6; y = 4; x + y = 10
x = 10; y = 5; x + y = 15
15

Just like the map class, the reduce function is designed to be used in conjunction with Lambda Expressions for tighter code. Lambda Expressions are used to create anonymous functions. In a nutshell, these functions do not have the def keyword, instead have a lambda keyword, take any number of arguments and return a single value in the form of an expression. Click here to know more about Lambda Expressions.

There is an optional argument to the reduce() function, the initialisation value. This becomes the first argument to the specified function, and the first element of the iterable object becomes the second argument to the function.

>>> def addTwoNumbers(x, y):
	print("x = {}; y = {}; x + y = {}".format(x, y, x + y))
	return x + y

>>> functools.reduce( addTwoNumbers, [1, 2, 3, 4, 5] , 20)
x = 20; y = 1; x + y = 21
x = 21; y = 2; x + y = 23
x = 23; y = 3; x + y = 26
x = 26; y = 4; x + y = 30
x = 30; y = 5; x + y = 35
35

In the event that the iterable object is empty, the reduce() function returns this value without trying out the function. If there is no initialisation value provided and the iterable object is empty, Python raises a TypeError 'TypeError: reduce() of empty sequence with no initial value'.

In Python 2, reduce() is a builtin function instead of a function in module functools. The usage is the same as in Python 3. It is worth noting that the Documentation advises against the usage of reduce() function. As it says, an explicit for loop is more readable.


See also:

Buffer this pageShare on FacebookPrint this pageTweet about this on TwitterShare on Google+Share on LinkedInShare on StumbleUpon

Leave a Reply