# 8.6. Loop For Nested¶

• Loop inside a loop

• Used to iterate over nested data

## 8.6.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]


## 8.6.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


## 8.6.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


## 8.6.4. List of Pairs¶

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


## 8.6.5. List of Threes¶

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


## 8.6.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)


## 8.6.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


## 8.6.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

## 8.6.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


## 8.6.10. Assignments¶

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

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

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

>>> assert result is not Ellipsis, \
'Assign your result to variable result'
>>> assert type(result) is dict, \
'Variable result has invalid type, should be 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
"""

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