9.3. Loop Nested Sequences

9.3.1. Convention

  • 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

9.3.2. Nested Loops

You can have loop inside a loop:

>>> 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

9.3.3. List of Pairs

>>> DATA = [('commander', 'Melissa Lewis'),
...         ('botanist', 'Mark Watney'),
...         ('chemist', 'Alex Vogel')]
>>>
>>> for pair in DATA:
...     role = pair[0]
...     name = pair[1]
...     print(f'{role} -> {name}')
commander -> Melissa Lewis
botanist -> Mark Watney
chemist -> Alex Vogel

9.3.4. 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:
...     features = row[0:4]
...     label = row[4]
...     print(f'{label} -> {sum(features)}')
setosa -> 10.2
versicolor -> 13.9
virginica -> 16.599999999999998

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

9.3.6. 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 obj in DATA:
...     print(obj)
('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 obj in DATA:
...     for element in obj:
...         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 obj in DATA:
...     if type(obj) in (list, tuple, set, frozenset):
...         for element in obj:
...             print(element)
...     else:
...         print(obj)
Mark
Watney
Lewis
69
13.37
True
None
False

9.3.7. Assignments

Code 9.11. Solution
"""
* Assignment: Loop Nested Mean
* Required: yes
* 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

    >>> result is not Ellipsis
    True
    >>> type(result)
    <class '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'),
]

# float: arithmetic mean from `Sepal length` column
result = ...

Code 9.12. Solution
"""
* Assignment: Loop Nested Unique Keys
* Required: yes
* 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

    >>> result is not Ellipsis
    True
    >>> type(result) in (set, list, tuple, frozenset)
    True
    >>> 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'},
]

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

Code 9.13. Solution
"""
* Assignment: Loop For Text
* Required: no
* 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. adverbs (words ending with "ly")
        b. sentences
        c. words
        d. letters
        e. characters (including spaces inside sentences, but not comas `,`)
        f. comas (`,`)
    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. przysłówków (słów zakończonych na "ly")
        b. zdań
        c. słów
        d. liter
        e. znaków (łącznie ze spacjami wewnątrz zdań, ale bez przecinków `,`)
        f. przecinków (`,`)
    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

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

    >>> type(result)
    <class 'dict'>

    >>> print(result)  # doctest: +NORMALIZE_WHITESPACE
    {'sentences': 7,
     'words': 71,
     'characters': 347,
     'letters': 283,
     'commas': 1,
     'adverbs': 0}
"""

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
"""

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