For loops: tricks

Unit 04

A few additional tricks for mastering for loops

for loops are useful in almost any data analysis, and mastering them can greatly enhance your ability to manipulate and process data. In Assignment #2 you got to know for loops of the kind

names = ['Alice', 'Bob', 'Charlie']
for name in names:
    print(name)
Alice
Bob
Charlie

Here are a few tricks and techniques that go beyond what you’ve learned so far to help you make the most of them:

Multiple iterators:

Sometimes you want to iterate over multiple variables at the same time. You can achieve this using the zip() function. This allows you to process corresponding elements from different lists simultaneously.

names = ['Alice', 'Bob', 'Charlie']
ages = [25, 30, 22]
for name, age in zip(names, ages):
    print(f'{name} is {age} years old.')
Alice is 25 years old.
Bob is 30 years old.
Charlie is 22 years old.

Notice the tuple unpacking in the for statement.

Enumerate for index and value pairs:

When iterating through a sequence like a list or tuple, you can use the enumerate() function to access both the index and value of each element in the loop. This can be especially useful when you need to keep track of both the position and the value of elements in the sequence.

fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
   print(f'Index {index}: {fruit}')
Index 0: apple
Index 1: banana
Index 2: cherry

Iterate over indices alone

Iterating over the sequence elements themselves, like we did so far, is a particular feature of python. Most other programming languages iterate over the sequence indices alone. Because of that special feature in python it is considered to be ‘bad practice’ or unpythonic to iterate over indices alone. However, I regularly come across situations where I have to know the index of my current iteration. You can either achieve this with the enumerate() function above (my favourite), or you can explicitely only iterate over the sequence indices with the range() function by providing the length of your sequence, like so

fruits = ['apple', 'banana', 'cherry']
for index in range(len(fruits)):
   print(f'Index {index}: {fruits[index]}')
Index 0: apple
Index 1: banana
Index 2: cherry

Skipping and exiting:

You have read about the continue and break statements already, but since they are very useful sometimes, it is worthwhile highlighting them here again. Use the continue statement to skip the current iteration and jump to the next increment, and use the break statement to exit the loop altogether. At this point it is also important to remember that you can exit the loop with a return statement in case you’re coding a function.

numbers = [1, 2, 3, 4, 5, 6]
for num in numbers:
    if num % 2 == 0:
        continue  # Skip even numbers
    print(num)
1
3
5
def find_first_negative(numbers):
    """Find and return the first negative number in a list.

    Parameters
    ----------
        numbers (list): A list of integers.

    Returns
    -------
        int or None: The first negative number found in the list, or 
        None if no negative number is found.

    Examples
    --------
        >>> numbers = [1, 3, -2, 5, -7, 8]
        >>> result = find_first_negative(numbers)
        >>> if result is not None:
        ...     print(f"The first negative number is {result}.")
        ... else:
        ...     print("No negative number found in the list.")
        The first negative number is -2.
    """
    for number in numbers:
        if number < 0:
            return number  # Return the first negative number
    return None  # Return None if no negative number is found

Nesting loops:

You can nest for loops inside one another to traverse and manipulate multidimensional data structures like matrices or nested lists. This is particularly useful for working with more complex data.

matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
    for element in row:
        print(element)
1
2
3
4
5
6
7
8
9

List comprehensions:

List comprehensions are a powerful feature in Python for creating new lists by applying an expression to each item in an existing list. In other words, they are a condensed version of a for loop. Here is an example:

numbers = [1, 2, 3, 4, 5]
squared_numbers = [x**2 for x in numbers]

which is short for the following explicit for loop

numbers = [1, 2, 3, 4, 5]
squared_numbers = []

for x in numbers:
    squared_numbers.append(x**2)

In this example, the list comprehension makes the code much more concise and also more readable.

You can even include conditional expressions in the list comprehension,

numbers = [1, 2, 3, 4, 5]
even_numbers = [x for x in numbers if x % 2 == 0]

but you notice that the readability decreases if you start convoluting the list comprehension.

Since you can write any loop explicitely, knowing how to actively code list comprehensions is optional for this course. I just want you to know of them and to be able to understand simple list comprehensions if you come across them. In case you are more interested in exploring list comprehensions, you will find a cheat sheet with useful links to more in-depth tutorials here.

Learning checklist

  • I can implement any for loop, such as iterating over single/multiple iterators, iterating over indices and values, or over indices alone.
  • I know how to nest loops.
  • I can skip individual iterations or break the loop entirely using continue or break.
  • I have heard of list comprehensions.