Sequence Nesting

CSCI 1913 – Introduction to Algorithms, Data Structures, and Program Development
Adriana Picoral

Nesting

In python lists and tuples can hold lists, tuples, and strings.

people = [["Ana", 34, "B"],
          ["Peter", 23, "A"]]
  • How to retrieve first name "Ana"?
  • How to retrieve first name "Peter"?
  • How to retrieve the first letter of each first name?
  • How to retrieve integer 23?

Nesting

  • How to retrieve first name "Ana"?
people[0][0]
'Ana'
  • How to retrieve first name "Peter"?
people[1][0]
'Peter'

Nesting

  • How to retrieve the first letter of each first name?
people[0][0][0]
'A'
people[1][0][0]
'P'
  • How to retrieve integer 23?
people[1][1]
23

Nested for loops

We can often use nested for loops to iterate over nested lists, but we don’t always need nested for loops.

def years_to_months(year):
  return year * 12

def change_age(people):
  for person in people:
    person[1] = years_to_months(person[1])
    
def to_string(people):
  new_string = "Name\tAge\tGrade\n"
  for person in people:
    for item in person:
      new_string += f"{item}\t"
    new_string += "\n"
  return new_string

if __name__ == "__main__":
  
  people = [["Ana", 34, "B"],
            ["Peter", 23, "A"],
            ["Mary", 27, "A"],
            ["John", 21, "C"]]
            
  change_age(people)
  print(people)
  
  print(to_string(people))
[['Ana', 408, 'B'], ['Peter', 276, 'A'], ['Mary', 324, 'A'], ['John', 252, 'C']]
Name    Age Grade
Ana 408 B   
Peter   276 A   
Mary    324 A   
John    252 C   

Exercise

Write a Python function called calculate_class_average that takes in as argument a nested list representing student grades and calculates the average of the averages. Name your file grade_average.py.

gradebook = [
    [85, 92, 78, 90], # Student 1's grades
    [88, 76, 95, 82], # Student 2's grades  
    [90, 87, 93, 89], # Student 3's grades
    [72, 68, 84, 79]  # Student 4's grades
]

The function should return the average grade across all students in the class (rounded to 2 decimal places).

assert calculate_class_average(gradebook) == 84.25

Solution

def calculate_class_average(grades):
    total = 0
    for student in grades:
        total += sum(student)/len(student)
    return round(total/len(grades), 2)

Nested object change

What will the code below output?

def create_nested_lists(rows, columns):
  one_row = [0] * columns
  return rows * [one_row]

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  my_list[0][0] = -1
  print(my_list)

Nested object change

What will the code below output?

def create_nested_lists(rows, columns):
  one_row = [0] * columns
  return rows * [one_row]

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  my_list[0][0] = -1
  print(my_list)
[[-1, 0, 0, 0, 0], [-1, 0, 0, 0, 0], [-1, 0, 0, 0, 0]]

Same problem

def create_nested_lists(rows, columns):
  return rows * [[0] * columns]

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  my_list[0][0] = -1
  print(my_list)
[[-1, 0, 0, 0, 0], [-1, 0, 0, 0, 0], [-1, 0, 0, 0, 0]]

Solution for the previous problem

def create_nested_lists(rows, columns):
  new_list = []
  for _ in range(rows):
    new_list.append([0] * columns)
  return new_list

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  my_list[0][0] = -1
  print(my_list)
[[-1, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Solution for the previous problem

With list comprehension:

def create_nested_lists(rows, columns):
  return [ [0] * columns for _ in range(rows)]

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  my_list[0][0] = -1
  print(my_list)
[[-1, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Deep copy vs. shallow copy

When copying a list, the nested lists will still refer to the same objects as in the original list.

def create_nested_lists(rows, columns):
  return [ [0] * columns for _ in range(rows)]

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  print(my_list)
  
  another_list = my_list.copy()
  
  another_list[0][0] = -1
  
  print(my_list)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
[[-1, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]

Deep copy

To recursively copy all objects, you need a deep copy.

import copy

def create_nested_lists(rows, columns):
  return [ [0] * columns for _ in range(rows)]

if __name__ == "__main__":
  my_list = create_nested_lists(3, 5)
  print(my_list)
  
  another_list = copy.deepcopy(my_list)
  
  another_list[0][0] = -1
  
  print(my_list)
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]