Python's Iteration Tools: filter(), islice(), map() and zip()

Introduction

Python has touched the hearts of many software developers around the world, thanks to its utility and simplicity.

Python provides its users with a number of useful functions and data structures that make working with data easier, including tools used to efficiently loop through data - known as itertools.

This guide will show you how to use Python itertools to iterate through objects via:

  • filter() - The filter() function takes in a provided sequence or iterable along with a filtering criteria (a function or lambda). It then tests every element in the sequence to determine if the element fits the filtering criteria, returning only the elements that match that criteria.
  • islice() - The islice() function allows the user to loop through an iterable with a start and stop, and returns a generator.
  • map() - The map() function creates an iterable map object that applies a specified transformation to every element in a chosen iterable.
  • zip() - The zip() function takes two iterable objects and returns a tuple of paired elements. The first item in both iterables is paired, the second item in both iterables is paired together, and so on.

We'll start by defining iterable objects and iteration functions and then proceed to look at some examples of the four iteration functions mentioned above.

Note: As of Python 3, filter(), map() and zip() are functionally equivalent to Python 2's itertools functions ifilter(), imap() and izip(). They all return iterators and don't require imports.

islice() wasn't ported into the built-in namespace of Python 3. You'll still have to import the itertools module to use it.

What are Iterable Objects?

An iterable object/iterable can be defined as a container holding data that can be looped/iterated over. Iterable objects in Python include lists, sets, tuples and dictionaries.

Typically, when we work with iterable objects, we loop through them using basic tools like for loops. We often ignore the features and tools a language may have that can help us with iterative tasks. Iteration tools offer efficient, standardized functions (similar to the functions you'd see in functional programming languages like Haskell) that integrate with other iterative functions to simplify iterative tasks down to just a few lines of code.

The filter() Function

filter() is a built-in function, and it allows us to take a group of iterable items and test if the elements within the iterable meet your specified filter criteria:

filter(function, iterable)

Since filter() returns a generator (filter object) - we'll wrap it in a list() to convert it back to a simple list. If we were to do filtering via for and if statements, it'd look something like:

# Create a simple list numbered 0 to 10
number_list = [x for x in range(0,10)]

# Will filter for even numbers
even_numbers = []
for number in number_list:
    if number%2 == 0:
        even_numbers.append(number)

print(even_numbers)

This results in:

[0, 2, 4, 6, 8]

By contrast, we could've achieved this same result using filter(), and passing in the same condition. If the condition is satisfied and True is returned, it's not filtered out. If the condition isn't satisfied and False is returned, the element of the iterable is filtered out.

This condition can be supplied either as an anonymous function - lambda or a standalone function:

number_list = [x for x in range(0,10)]

filtered_list = list(filter(lambda number: number % 2 == 0, number_list))

print(filtered_list)

When provided with a lambda - the number is an element of the iterable we're currently filtering. For each number, we check if it's divisible by 2. If so - it's included in the new output:

[0, 2, 4, 6, 8]

As long as the function returns True or False, you can extract the function to stand alone and simply reference it here instead of using a lambda:

number_list = [x for x in range(0,10)]

def is_even(number):
    return number%2==0
    
filtered_list = list(filter(is_even, number_list))
print(filtered_list)

Another function similar to filter(), called filterfalse(), can be found in itertools. This is a counterpart of filter() that returns the elements that don't satisfy the condition. After importing the function from itertools we can use our past code and apply filterfalse() to get just the odd numbers from the list:

from itertools import filterfalse
number_list = [x for x in range(0,10)]

filtered_list = list(filterfalse(lambda number: number % 2 == 0, number_list))

print(filtered_list)

This results in a filtered list of odd numbers:

[1, 3, 5, 7, 9]

Instead of an anonymous function, you can also use a standalone function here too:

from itertools import filterfalse
number_list = [x for x in range(0,10)]

def is_even(number):
    return number%2==0

filtered_list = list(filterfalse(is_even, number_list))

print(filtered_list)

The islice() Function

The islice() function is part of the itertools library, and it takes an iterable object and returns a segment from it, between the elements defined by the start and end arguments given to the function:

itertools.islice(iterable, start, end)

Let's islice() a string. Since this returns a generator, we'll wrap it in a list to contain the result as well. If you omit the start argument - the function will slice until the mandatory provided end argument. If both are provided, it'll slice between them and return that segment:

from itertools import islice
old_string = "I need this, but not this"
print(list(islice(old_string, 11)))

Here, we've sliced the old_string from its start to the 11th element:

['I', ' ', 'n', 'e', 'e', 'd', ' ', 't', 'h', 'i', 's']

If we provide a start argument, though, we can slice a specific segment out:

from itertools import islice
old_string = "I need this, but not this"

print(list(islice(old_string, 7, 11)))
Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

['t', 'h', 'i', 's']

Usually, when working with iterables - we want to end up with an iterable, such as a list. Though slicing is a common operation for strings as well, in which case, we typically want a string - not a list. Thankfully, it's easy to join() the elements of the list back into a string:

print(''.join(list(islice(old_string, 0, 11))))

Here, we've joined each element to an empty string, which results in the sliced segment being converted into a string:

I need this

The map() Function

The map function takes an iterable object and a function that applies a transformation to all the items of the iterable:

map(function, iterable)

The map() function is included in Python's built-in functions, so there's no need to import anything. map() offers the exact same functionality as imap() from the itertools module in Python 2.

Generally speaking - it's very useful when you'd like to perform batch-transformations on each element of an iterable. Each element maps to a transformed version of that element, or a result of another operation performed by or on that element.

Say, you'd like to raise every integer element to the power of 2:

number_list = [x for x in range(0,10)]

numbers_powered = []
for number in number_list:
    numbers_powered.append(number**2)
print(numbers_powered)

This results in a sequence of:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Now, we can simplify this with a map():

print(list(map(lambda x: x**2, number_list)))

For each element in the number_list iterable - the element is raised to the power of two, and put in a new list:

[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

Of course, instead of an anonymous function, you can also define other functions instead:

number_list = [x for x in range(0,10)]

def function(number):
    print("Performing transformation on number ", number)
    return number**2

print('Original list: ', number_list)

mapped_list = list(map(function, number_list))

print('Transformed list: ', mapped_list)

This results in:

Original list:  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Performing transformation on number  0
Performing transformation on number  1
Performing transformation on number  2
Performing transformation on number  3
Performing transformation on number  4
Performing transformation on number  5
Performing transformation on number  6
Performing transformation on number  7
Performing transformation on number  8
Performing transformation on number  9
Transformed list:  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

The zip() Function

The zip() function accepts 0..n iterable objects and creates 0..n tuples containing the nth element of each of these iterables:

zip(iterable_1, iterable_2, iterable_3...)

It's a built-in function since Python 3 and offers the same functionality izip() from the itertools module offered in Python 2.

Let's zip() together a list of names and a list of IDs, where the first name is zipped with the first ID, the second name is zipped with the second ID, et cetera:

names_list = ['Francis', 'Drake', 'Alexander', 'Robert', 'Elon']
id_list = ['001', '002', '003', '004', '005']

print(list(zip(names_list,id_list)))

This results in:

[('Francis', '001'), ('Drake', '002'), ('Alexander', '003'), ('Robert', '004'), ('Elon', '005')]

Note: If these iterables are not of the same shape, such as names_list having 5 elements, and id_list having 10 elements, only the first 5 would be mapped, while the rest of id_list would be ignored. The longest common sequence will be mapped.

As usual, this returns a generator - so we've wrapped it in a list().

The same functionality and behavior is present with more than two iterables - you can provide an unbounded number of them, in fact:

names_list = ['Francis', 'Drake', 'Alexander', 'Robert', 'Elon']
last_name_list = ['Brown', 'Johnson', 'Tiedemann', 'Mann']
id_list = ['001', '002', '003', '004', '005']

zipped_list = list(zip(names_list, last_name_list, id_list))
print(zipped_list)
[('Francis', 'Brown', '001'), ('Drake', 'Johnson', '002'), ('Alexander', 'Tiedemann', '003'), ('Robert', 'Mann', '004')]

Since the names_list is of length 5, while the other two iterables are of length 4 - the last element of names_list doesn't have a pair.

This is a great tool to group related items that appear in different contexts.

Conclusion

Python comes with a number of built-in functions that help engineers easily and efficiently manipulate data, through a high-level API. Iteration is a very common operation - and Python's iteration tools are very handy for one-line, functional-style operations on elements.

In this guide, we've taken a look at the filter(), map(), islice() and zip() functions.

Although islice() resides in the itertools module, and isn't present in the built-in namespace - it's a type of function you'll commonly use for sub-sequencing other sequences, and is commonly used with the other functions highlighted in the guide.

Last Updated: October 10th, 2023
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms