11.2. Loop For Nested

  • Loop inside a loop

  • Used to iterate over nested data

11.2.1. Recap

>>> DATA = [1, 2, 3]
>>>
>>> for obj in DATA:
...     print(f'{obj=}')
obj=1
obj=2
obj=3
>>> DATA = [[1, 2, 3],
...         [4, 5, 6],
...         [7, 8, 9]]
>>>
>>>
>>> for obj in DATA:
...     print(f'{obj=}')
obj=[1, 2, 3]
obj=[4, 5, 6]
obj=[7, 8, 9]

11.2.2. Nested Loops

You can have loop inside a loop:

>>> DATA = [[1, 2, 3],
...         [4, 5, 6],
...         [7, 8, 9]]
>>>
>>>
>>> for row in DATA:  
...     for value in row:
...         print(f'{value}', end=' ')
...     print()
1 2 3
4 5 6
7 8 9

11.2.3. List of List

  • Matrix

  • Suggested variable name: row

>>> DATA = [[1, 2, 3],
...         [4, 5, 6],
...         [7, 8, 9]]
>>>
>>>
>>> for row in DATA:
...     a = row[0]
...     b = row[1]
...     c = row[2]
...     print(f'{a=} {b=} {c=}')
a=1 b=2 c=3
a=4 b=5 c=6
a=7 b=8 c=9

11.2.4. List of Pairs

>>> users = [
...     ('commander', 'Melissa Lewis'),
...     ('botanist', 'Mark Watney'),
...     ('pilot', 'Rick Martinez'),
... ]
>>>
>>>
>>> for user in users:
...     role = user[0]
...     name = user[1]
...     print (f'{role=}, {name=}')
role='commander', name='Melissa Lewis'
role='botanist', name='Mark Watney'
role='pilot', name='Rick Martinez'

11.2.5. List of Threes

>>> users = [
...     ('commander', 'Melissa', 'Lewis'),
...     ('botanist', 'Mark', 'Watney'),
...     ('pilot', 'Rick', 'Martinez'),
... ]
>>>
>>>
>>> for user in users:
...     role = user[0]
...     firstname = user[1]
...     lastname = user[2]
...     print (f'{role=}, {firstname=}, {lastname=}')
role='commander', firstname='Melissa', lastname='Lewis'
role='botanist', firstname='Mark', lastname='Watney'
role='pilot', firstname='Rick', lastname='Martinez'

11.2.6. List of Sequence

>>> DATA = [
...     (5.1, 3.5, 1.4, 0.2, 'setosa'),
...     (5.7, 2.8, 4.1, 1.3, 'versicolor'),
...     (6.3, 2.9, 5.6, 1.8, 'virginica'),
... ]
>>>
>>>
>>> for row in DATA:
...     sepal_length = row[0]
...     sepal_width = row[1]
...     petal_length = row[2]
...     petal_width = row[3]
...     species = row[4]
...     total = sepal_length + sepal_width + petal_length + petal_width
...     print(f'{species} -> {total}')
setosa -> 10.2
versicolor -> 13.9
virginica -> 16.599999999999998
>>> DATA = [
...     (5.1, 3.5, 1.4, 0.2, 'setosa'),
...     (5.7, 2.8, 4.1, 1.3, 'versicolor'),
...     (6.3, 2.9, 5.6, 1.8, 'virginica'),
... ]
>>>
>>>
>>> for row in DATA:
...     values = row[0:4]
...     species = row[4]
...     print(f'{species=}, {values=}')
species='setosa', values=(5.1, 3.5, 1.4, 0.2)
species='versicolor', values=(5.7, 2.8, 4.1, 1.3)
species='virginica', values=(6.3, 2.9, 5.6, 1.8)

11.2.7. Mixed

Let's analyze the following example. We received data as follows:

>>> DATA = [('Mark', 'Watney'), 'Lewis', 69, 13.37, [True, None, False]]

The desired format should be:

Mark
Watney
Lewis
69
13.37
True
None
False

How to convert DATA to desired format?

Iterating over list with scalar and vector values - simple loop:

>>> DATA = [('Mark', 'Watney'), 'Lewis', 69, 13.37, [True, None, False]]
>>>
>>> for current in DATA:
...     print(current)
('Mark', 'Watney')
Lewis
69
13.37
[True, None, False]

Iterating over list with scalar and vector values - nested loop:

>>> DATA = [('Mark', 'Watney'), 'Lewis', 69, 13.37, [True, None, False]]
>>>
>>> 
... for current in DATA:
...     for element in current:
...         print(element)
Mark
Watney
W
a
t
n
e
y
Traceback (most recent call last):
TypeError: 'int' object is not iterable

Iterating over list with scalar and vector values - smart loop:

>>> DATA = [('Mark', 'Watney'), 'Lewis', 69, 13.37, [True, None, False]]
>>>
>>> for current in DATA:
...     if type(current) in (list, tuple, set):
...         for inside in current:
...             print(inside)
...     else:
...         print(current)
Mark
Watney
Lewis
69
13.37
True
None
False

11.2.8. Good Practices

  • row - best for nested loops with sequence inside

  • Conventions for rows and columns:

    • row - row (all elements)

    • column - current column element from row sequence

    • i - row number

    • j - column number

    • x - row number

    • y - column number

    • outer - for outer loop element

    • inner - for inner loop element

  • Note that i may interfere with i used as loop counter

11.2.9. Use Case - 0x01

>>> for row in [1, 2, 3]:  
...     print()
...
...     for column in ['A', 'B', 'C']:
...         print(f'{column}{row}', end=' ')
A1 B1 C1
A2 B2 C2
A3 B3 C3

11.2.10. Assignments

"""
* Assignment: Loop Nested Mean
* Type: class assignment
* Complexity: easy
* Lines of code: 5 lines
* Time: 5 min

English:
    1. Calculate mean `sepal_length` value
    2. Run doctests - all must succeed

Polish:
    1. Wylicz średnią wartość `sepal_length`
    2. Uruchom doctesty - wszystkie muszą się powieść

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert result is not Ellipsis, \
    'Assign your result to variable `result`'
    >>> assert type(result) is float, \
    'Variable `result` has invalid type, should be float'

    >>> result
    5.911111111111111
"""

DATA = [
    ('sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'),
    (5.8, 2.7, 5.1, 1.9, 'virginica'),
    (5.1, 3.5, 1.4, 0.2, 'setosa'),
    (5.7, 2.8, 4.1, 1.3, 'versicolor'),
    (6.3, 2.9, 5.6, 1.8, 'virginica'),
    (6.4, 3.2, 4.5, 1.5, 'versicolor'),
    (4.7, 3.2, 1.3, 0.2, 'setosa'),
    (7.0, 3.2, 4.7, 1.4, 'versicolor'),
    (7.6, 3.0, 6.6, 2.1, 'virginica'),
    (4.6, 3.1, 1.5, 0.2, 'setosa'),
]

# Arithmetic mean from `sepal_length` column
# type: float
result = ...

"""
* Assignment: Loop Nested Unique Keys
* Type: class assignment
* Complexity: medium
* Lines of code: 3 lines
* Time: 8 min

English:
    1. Collect unique keys from all rows in one sequence `result`
    2. Run doctests - all must succeed

Polish:
    1. Zbierz unikalne klucze z wszystkich wierszy w jednej sekwencji `result`
    2. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `row.keys()`
    * Compare solutions with :ref:`Micro-benchmarking use case`

Tests:
    >>> import sys; sys.tracebacklimit = 0

    >>> assert result is not Ellipsis, \
    'Assign your result to variable `result`'
    >>> assert type(result) in (set, list, tuple), \
    'Variable `result` has invalid type, should be on of (set, list, tuple)'

    >>> sorted(result)
    ['petal_length', 'petal_width', 'sepal_length', 'sepal_width', 'species']
"""

DATA = [
    {'sepal_length': 5.1, 'sepal_width': 3.5, 'species': 'setosa'},
    {'petal_length': 4.1, 'petal_width': 1.3, 'species': 'versicolor'},
    {'sepal_length': 6.3, 'petal_width': 1.8, 'species': 'virginica'},
    {'sepal_length': 5.0, 'petal_width': 0.2, 'species': 'setosa'},
    {'sepal_width': 2.8, 'petal_length': 4.1, 'species': 'versicolor'},
    {'sepal_width': 2.9, 'petal_width': 1.8, 'species': 'virginica'},
]

# Unique keys from DATA dicts
# type: set[str]
result = ...

"""
* Assignment: Loop For Text
* Type: homework
* Complexity: medium
* Lines of code: 14 lines
* Time: 13 min

English:
    1. Given is text of the "Moon Speech" by John F. Kennedy's [1]
    2. Sentences are separated by period (`.`)
    3. Clean each sentence from whitespaces at the beginning and at the end
    4. Words are separated by spaces
    5. Print the total number in whole text:
        a. sentences
        b. words
        c. letters
        d. characters (including spaces inside sentences, but not comas `,`)
        e. commas (`,`)
        f. adverbs (words ending with "ly")
    6. Run doctests - all must succeed

Polish:
    1. Dany jest tekst przemówienia "Moon Speech" wygłoszonej
       przez John F. Kennedy'ego [1]
    2. Zdania oddzielone są kropkami (`.`)
    3. Każde zdanie oczyść z białych znaków na początku i końcu
    4. Słowa oddzielone są spacjami
    5. Wypisz także ile jest łącznie w całym tekście:
        a. zdań
        b. słów
        c. liter
        d. znaków (łącznie ze spacjami wewnątrz zdań, ale bez przecinków `,`)
        e. przecinków (`,`)
        f. przysłówków (słów zakończonych na "ly")
    6. Uruchom doctesty - wszystkie muszą się powieść

References:
    [1] Kennedy, J.F. Moon Speech - Rice Stadium.
        Year: 1962.
        Retrieved: 2021-03-06.
        URL: http://er.jsc.nasa.gov/seh/ricetalk.htm

Hints:
    * str.split()
    * str.strip()
    * str.replace()
    * str.count()
    * str.endswith()
    * list()
    * len()

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from pprint import pprint

    >>> assert result is not Ellipsis, \
    'Assign your result to variable `result`'
    >>> assert type(result) is dict, \
    'Variable `result` has invalid type, should be dict'

    >>> pprint(result)
    {'adverbs': 0,
     'characters': 347,
     'commas': 1,
     'letters': 283,
     'sentences': 7,
     'words': 71}
"""

TEXT = """
    We choose to go to the Moon.
    We choose to go to the Moon in this decade and do the other things.
    Not because they are easy, but because they are hard.
    Because that goal will serve to organize and measure the best of our energies and skills.
    Because that challenge is one that we are willing to accept.
    One we are unwilling to postpone.
    And one we intend to win
"""

# Number of occurrences of each grammar object
# type: dict[str,int]
result = {
    'adverbs': 0,
    'characters': 0,
    'commas': 0,
    'letters': 0,
    'sentences': 0,
    'words': 0,
}