For Loops and Lists

Being able to repeat commands or statements is a very powerful tool. We learned this previously when working with while loops. For loops work in a similar manner; however, for loops are used to iterate(repeat code for every element or item) over a sequence or other iterable objects. If you were to use a for loop to go run through a sequence, such as a list, it would look like:

For Loop Structure

Image Source

On each iteration, the item or element that is next in the sequence is assigned to the iterating_var. Then, the statement(s) are executed. The loop continues in this fashion until all the sequence has been processed. But what is a sequence anyways? Well, a sequence in Python is an ordered collection. Sequence types in Python include strings (sequence of characters), lists (sequence of elements), and tuples (finite ordered list). Lists are very similar to arrays in languages like Java; however, arrays are fixed sized data structures whereas lists can grow dynamically as you add elements. Tuples are like lists lists, but instead of being growable, they are finite, immutable, or not able to be modified once created. Let’s look at a few basic examples of using for loops to iterate over these data structures:

In [56]:
for number in range(1,6):
    print(number)
1
2
3
4
5
In [57]:
my_things = "Hello, world!"
for thing in my_things:
    print(thing)
H
e
l
l
o
,
 
w
o
r
l
d
!
In [58]:
my_things = (1, 5, 9)
for thing in my_things:
    print(thing)
1
5
9
In [59]:
fruit = ["tomato", "apple", "mango", "banana"]
for item in fruit:
    print("{}".format(item))
tomato
apple
mango
banana

Notice that in all the loops above are iterating over the values contained inside the sequence and not the index. For the purposes of this tutorial, let us define a function to help print out a list. This is going to be quite helpful as we move forward in exploring lists. We will cover the details of how functions work in a later tutorial, but a function acts as a reusable reference to a set of statements, much like a loop. To use a function, we can simply call it by referencing its name, followed by parentheses and any parameters or data it requires.
The function below will print the contents of a given list with both the value AND the index. This is done using the enumerate function that is built in to Python. Enumerate will keep track of the index of each value as the loop iterates. The enumerate function produces a two item tuple on demand. The first item in the tuple is the index, followed by the value.

In [60]:
fruit = ["tomato", "apple", "mango", "banana"]
def output_list(my_list): 
    for index, value in enumerate(my_list):
        print("{}:{}".format(index,value))

output_list(fruit)
0:tomato
1:apple
2:mango
3:banana

List Functions

Now that we can output our list easily, let’s check out some of the common built-in functions for lists. All the functions can be found here.

Adding Elements

To add an element to a list, you can use the append function:

In [61]:
fruit.append("canteloup")
output_list(fruit) 
0:tomato
1:apple
2:mango
3:banana
4:canteloup

Notice that the new element is added to the end of the list it is being appended to. The append function adds to the end of a list, but what if we want to add or change a value elsewhere? To change the value at any given index, we can simply access the index and assign a new value:

In [62]:
fruit = ["tomato", "apple", "mango", "banana"]
fruit[2] = "bluberries"
output_list(fruit)
0:tomato
1:apple
2:bluberries
3:banana

Elements can also be inserted at specified locations by using the insert function. This will shift all the elements in the list that come after the specified index over one, so like the remove function, be aware of this extra computation in case your lists are very large!

In [63]:
fruit = ["tomato", "apple", "mango", "banana"]
print(fruit[2])
fruit.insert(2,"bluberries")
output_list(fruit)
print(fruit[2])
mango
0:tomato
1:apple
2:bluberries
3:mango
4:banana
bluberries

Removing Elements

Apart from adding, we can also remove elements:

In [64]:
fruit = ["tomato", "apple", "mango", "banana"]
fruit.append("canteloup")
fruit.append("tomato")
fruit.remove("tomato")
output_list(fruit)
0:apple
1:mango
2:banana
3:canteloup
4:tomato

The remove function isn't as intuitive as append. The remove function will scan the list, starting with the first element, until it finds the first occurrence of the value you want to remove. Notice that in the above example, an extra tomato is appended to the list, but when we remove tomato, it only removes the first occurrence and not the most recent one we added.

If you want to remove every occurrence, you must loop through the list manually:

In [65]:
fruit = ["tomato", "apple", "mango", "banana"]
fruit.append("canteloup")
fruit.append("tomato")
for item in fruit:
    if item == 'tomato':
        fruit.remove('tomato')
output_list(fruit)
0:apple
1:mango
2:banana
3:canteloup

If you want to delete a specific item, you can do so by using del followed by what you want to delete. This works for many objects other than just lists; however, to delete something inside a list, you can use the following syntax: del my_list[index_of_value]. The code below deletes the value at index 5 in the fruit list. This is the most recent "tomato" added:

In [66]:
fruit = ["tomato", "apple", "mango", "banana"]
fruit.append("canteloupe")
fruit.append("tomato")
del fruit[5]
output_list(fruit)
0:tomato
1:apple
2:mango
3:banana
4:canteloupe

With the del functionality and the enumerate function, we can improve the performance of our previous attempt at removing all occurrences of a value:

In [67]:
fruit = ["tomato", "apple", "mango", "banana"]
fruit.append("canteloup")
fruit.append("tomato")
for i, item in enumerate(fruit):
    if item == 'tomato':
        del fruit[i]
output_list(fruit)
0:apple
1:mango
2:banana
3:canteloup

Finding the Index

Another useful function apart of lists is the index function. This will return the index of the first occurrence of the specified value in the list.

In [68]:
fruit = ["tomato", "apple", "mango", "banana", "tomato"]
print(fruit.index("tomato"))
print(fruit.index("mango"))
0
2

Keep in mind though, it will throw an exception if the value is not in the list! We can avoid that by checking if the value is in the list first.

In [69]:
fruit = ["tomato", "apple", "mango", "banana", "tomato"]
print(fruit.index("cat"))
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-69-afc17d332d69> in <module>()
      1 fruit = ["tomato", "apple", "mango", "banana", "tomato"]
----> 2 print(fruit.index("cat"))

ValueError: 'cat' is not in list
In [76]:
fruit = ["tomato", "apple", "mango", "banana", "tomato"]
if "cat" in fruit:
    print(fruit.index("cat"))
else:
    print("Oops, that isn't in the list!")
Oops, that isn't in the list!

Slicing

We can also slice parts of a list just like with strings! Thankfully, it’s the exact same syntax: sequence[start:end:optional_step]

In [77]:
fruit = ["tomato", "apple", "mango", "banana"]
print(fruit[:])
print(fruit[:1]) 
print(fruit[-2:]) # last two elements from the end
print(fruit[::2]) # every other element
print(fruit[::-1]) # reverse!
del fruit[1:3] # Lists are mutable, unlike strings, so this deletion modifies the existing list!
print(fruit)
['tomato', 'apple', 'mango', 'banana']
['tomato']
['mango', 'banana']
['tomato', 'mango']
['banana', 'mango', 'apple', 'tomato']
['tomato', 'banana']

More Functions for Lists

There is a great deal more functions that are not part of the list object, but are a part of the general Python language. These functions can still apply to lists! The filter function is particularly useful. This function removes all elements of the list that do not pass the given lambda expression. We will save the discussion about lambda expressions for a later date, but you can think of them as a mini function.

In [78]:
fruit = ["tomato", "apple", "mango", "banana", "canteloup","tomato"]
fruit = list(filter(lambda item: item != 'tomato', fruit))
output_list(fruit)
0:apple
1:mango
2:banana
3:canteloup

List Comprehensions

List comprehensions are a very concise and readable way to create lists in Python. It is done by using a set of square brackets that have an expression and at least for clause inside. The first for clause, can be followed by more optional for or if clauses: [expression for thing in things]

In [79]:
# this way of creating a list requires more code
numbers = []
for i in range(1, 11):
    numbers.append(i * 2)
output_list(numbers)
0:2
1:4
2:6
3:8
4:10
5:12
6:14
7:16
8:18
9:20
In [80]:
# list comprehension is much shorter and easier to read
numbers = [i * 2 for i in range(1,11)]
output_list(numbers)
0:2
1:4
2:6
3:8
4:10
5:12
6:14
7:16
8:18
9:20

Bubble Sort Example

Remember from our previous lectures that bubble sort is a simple sorting algorithm that compares each pair of elements in a sequence and swaps them if they are out of order. This is repeated until no more swaps are made.

In [81]:
import random

# make a random list of 10 numbers
numbers = [random.randint(0,100) for i in range(10)]
print(numbers)

# loop until no more swaps are made, set swap to True to force the first loop
swap = True
while swap:
    swap = False
    # Loop through the indicies of the list of numbers.  
    # Note that we have to exclude the last number in the list so we don't get an 
    #    index out of bounds exception when comparing adjacent numbers
    for i in range(len(numbers)-1):
        # if this number is bigger than the next, swap
        if numbers[i] > numbers[i+1]:
            # made a swap so set the loop invarient to True so we repeat the sort
            swap = True
            # need a temporary holding place
            temp = numbers[i]
            numbers[i] = numbers[i+1]            
            numbers[i+1] = temp
print(numbers)
[59, 28, 97, 10, 55, 77, 56, 12, 98, 47]
[10, 12, 28, 47, 55, 56, 59, 77, 97, 98]