4.4. Idiom Enumerate

  • Enumerate sequences

  • Built-in generator like (lazy evaluated)

  • enumerate(*iterables)

  • required *iterables - 1 or many sequences or iterator object

  • Return an enumerate object

  • The enumerate object yields pairs containing a count (from start, which defaults to zero) and a value yielded by the iterable argument.

>>> from inspect import isgeneratorfunction, isgenerator
>>>
>>>
>>> isgeneratorfunction(enumerate)
False
>>> result = enumerate(['a', 'b', 'c'])
>>> isgenerator(result)
False

4.4.1. Problem

>>> months = ['January', 'February', 'March']
>>> result = []
>>>
>>> i = 0
>>>
>>> for month in months:
...     result.append((i, month))
...     i += 1
>>>
>>> result
[(0, 'January'), (1, 'February'), (2, 'March')]

4.4.2. Solution

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months)
>>>
>>> list(result)
[(0, 'January'), (1, 'February'), (2, 'March')]

4.4.3. Lazy Evaluation

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months)
>>>
>>> next(result)
(0, 'January')
>>> next(result)
(1, 'February')
>>> next(result)
(2, 'March')
>>> next(result)
Traceback (most recent call last):
StopIteration

4.4.4. Dict Conversion

>>> months = ['January', 'February', 'March']
>>> result = enumerate(months)
>>>
>>> dict(result)
{0: 'January', 1: 'February', 2: 'March'}
>>> months = ['January', 'February', 'March']
>>> result = enumerate(months, start=1)
>>>
>>> dict(result)
{1: 'January', 2: 'February', 3: 'March'}
>>> months = ['January', 'February', 'March']
>>> result = {f'{i:02}':month for i,month in enumerate(months, start=1)}
>>>
>>> print(result)
{'01': 'January', '02': 'February', '03': 'March'}

4.4.5. Using in a Loop

>>> months = ['January', 'February', 'March']
>>>
>>> for i, month in enumerate(months, start=1):
...     print(f'{i} -> {month}')
1 -> January
2 -> February
3 -> March

4.4.6. Assignments

Code 4.11. Solution
"""
* Assignment: Idiom Enumerate Dict
* Type: class assignment
* Complexity: easy
* Lines of code: 1 lines
* Time: 2 min

English:
    1. Define `result: dict`
    2. Assign to `result` converted `DATA` to `dict`
    3. Use `enumerate()`
    4. Run doctests - all must succeed

Polish:
    1. Zdefiniu `result: dict`
    2. Przypisz do `result` przekonwertowane `DATA` do `dict`
    3. Użyj `enumerate()`
    4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `dict()`
    * `enumerate()`

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

    >>> assert type(result) is dict, \
    'Variable `result` has invalid type, should be dict'

    >>> assert all(type(x) is int for x in result.keys()), \
    'All dict keys should be int'

    >>> assert all(type(x) is str for x in result.values()), \
    'All dict values should be str'

    >>> result
    {0: 'setosa', 1: 'versicolor', 2: 'virginica'}
"""

DATA = ['setosa', 'versicolor', 'virginica']

# Dict with enumerated DATA
# type: dict[int,str]
result = ...

Code 4.12. Solution
"""
* Assignment: Idiom Enumerate Start
* Type: class assignment
* Complexity: easy
* Lines of code: 1 lines
* Time: 3 min

English:
    1. Convert `MONTH` into dict:
        a. Keys: month number
        b. Values: month name
    2. Use dict() and enumerate()
    3. Run doctests - all must succeed

Polish:
    1. Przekonwertuj `MONTH` w słownik:
        a. klucz: numer miesiąca
        b. wartość: nazwa miesiąca
    2. Użyj dict() i enumerate()
    3. Uruchom doctesty - wszystkie muszą się powieść

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

    >>> type(result)
    <class 'dict'>
    >>> 0 not in result
    True
    >>> 13 not in result
    True
    >>> result[1] == 'January'
    True

    >>> assert all(type(x) is int for x in result.keys())
    >>> assert all(type(x) is str for x in result.values())

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    {1: 'January',
     2: 'February',
     3: 'March',
     4: 'April',
     5: 'May',
     6: 'June',
     7: 'July',
     8: 'August',
     9: 'September',
     10: 'October',
     11: 'November',
     12: 'December'}
"""

MONTHS = ['January', 'February', 'March', 'April',
          'May', 'June', 'July', 'August', 'September',
          'October', 'November', 'December']

# number and month name
# type: dict[str,str]
result = ...

Code 4.13. Solution
"""
* Assignment: Idiom Enumerate ZeroPadded
* Type: class assignment
* Complexity: easy
* Lines of code: 1 lines
* Time: 3 min

English:
    1. Use dict comprehension and enumerate
    2. Convert `MONTH` into `result: dict[str,str]`:
        a. Keys: month number
        b. Values: month name
    3. Month number must be two letter string
       (zero padded) - `f'{number:02}'`
    4. Run doctests - all must succeed

Polish:
    1. Użyj rozwinięcia słownikowego i enumeracji
    2. Przekonwertuj `MONTH` w `result: dict[str,str]`:
        a. klucz: numer miesiąca
        b. wartość: nazwa miesiąca
    3. Numer miesiąca ma być dwuznakowym stringiem
       (wypełnij zerem) - `f'{number:02}'`
    4. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `enumerate()`
    * `f'{number:02}'`

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

    >>> type(result)
    <class 'dict'>
    >>> '00' not in result
    True
    >>> '13' not in result
    True
    >>> result['01'] == 'January'
    True

    >>> assert all(type(x) is str for x in result.keys())
    >>> assert all(type(x) is str for x in result.values())
    >>> assert all(len(x) == 2 for x in result.keys())

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    {'01': 'January',
     '02': 'February',
     '03': 'March',
     '04': 'April',
     '05': 'May',
     '06': 'June',
     '07': 'July',
     '08': 'August',
     '09': 'September',
     '10': 'October',
     '11': 'November',
     '12': 'December'}
"""

MONTHS = ['January', 'February', 'March', 'April',
          'May', 'June', 'July', 'August', 'September',
          'October', 'November', 'December']

# With zero-padded number and month name
# type: dict[str,str]
result = ...

Code 4.14. Solution
"""
* Assignment: Idiom Enumerate LabelEncoder
* Complexity: medium
* Lines of code: 3 lines
* Time: 5 min

English:
    1. From `DATA` separate header (first line) from other lines
    2. Split header by comma `,` into:
       a. `nrows` - number of rows
       b. `nfeatures` - number of features
       c. `class_labels` - species names
    3. Generate `result: dict[int,str]` from `class_labels`:
       a. 0: setosa
       b. 1: virginica
       c. 2: versicolor
    4. Use `enumerate()`
    5. Run doctests - all must succeed

Polish:
    1. Z `DATA` odseparuj nagłówek (pierwsza linia) od pozostałych linii
    2. Rozdziel nagłówek po przecinku `,` na:
       a. `nrows` - liczba wierszy
       b. `nfeatures` - liczba cech
       c. `class_labels` - nazwy gatunków
    3. Wygeneruj `result: dict[int,str]` z `class_labels`:
       a. 0: setosa
       b. 1: virginica
       c. 2: versicolor
    4. Użyj `enumerate()`
    5. Uruchom doctesty - wszystkie muszą się powieść

Hints:
    * `dict(enumerate())`
    * `str.splitlines()`
    * `str.split()`
    * `str.strip()`
    * line: "3,4,setosa,virginica,versicolor" is not an error
    * 3 - rows
    * 4 - number of features (values)
    * setosa,virginica,versicolor - if 0 then setosa, if 1 then virginica, etc.

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

    >>> assert result is not Ellipsis, \
    'Assign result to variable: `result`'
    >>> assert type(result) is dict, \
    'Variable `result` has invalid type, should be dict'
    >>> assert all(type(x) is int for x in result.keys()), \
    'All keys in `result` should be int'
    >>> assert all(type(x) is str for x in result.values()), \
    'All values in `result` should be str'

    >>> result  # doctest: +NORMALIZE_WHITESPACE
    {0: 'setosa', 1: 'virginica', 2: 'versicolor'}
"""

DATA = """3,4,setosa,virginica,versicolor
5.8,2.7,5.1,1.9,1
5.1,3.5,1.4,0.2,0
5.7,2.8,4.1,1.3,2"""

# values from file (note the list[tuple] format!)
# type: dict[int,str]
result = ...

Code 4.15. Solution
"""
* Assignment: Idiom Enumerate Impl
* Complexity: medium
* Lines of code: 7 lines
* Time: 13 min

English:
    1. Write own implementation of a built-in `enumerate()` function
    2. Define function `myenumerate` with parameters:
        a. parameter `iterable: list | tuple`
        b. parameter `start: int`
    3. Don't validate arguments and assume, that user will:
        a. always pass valid type of arguments
        b. iterable length will always be greater than 0
    4. Do not use built-in function `enumerate()`
    5. Run doctests - all must succeed

Polish:
    1. Zaimplementuj własne rozwiązanie wbudowanej funkcji `enumerate()`
    2. Zdefiniuj funkcję `myenumerate` z parametrami:
        a. parametr `iterable: list | tuple`
        b. parametr `start: int`
    3. Nie waliduj argumentów i przyjmij, że użytkownik:
        a. zawsze poda argumenty poprawnych typów
        b. długość iterable będzie większa od 0
    4. Nie używaj wbudowanej funkcji `enumerate()`
    5. Uruchom doctesty - wszystkie muszą się powieść

Hint:
    * https://github.com/python/cpython/blob/main/Objects/enumobject.c

Tests:
    >>> import sys; sys.tracebacklimit = 0
    >>> from inspect import isfunction
    >>> assert isfunction(myenumerate)

    >>> myenumerate(['January', 'February', 'March'])
    [(0, 'January'), (1, 'February'), (2, 'March')]

    >>> myenumerate(['January', 'February', 'March'], start=1)
    [(1, 'January'), (2, 'February'), (3, 'March')]

    >>> dict(myenumerate(['January', 'February', 'March'], start=1))
    {1: 'January', 2: 'February', 3: 'March'}
"""

# Write own implementation of a built-in `enumerate()` function
# Define function `myenumerate` with parameters: `iterable`, `start`
# type: Callable[[Iterable, int], list[tuple]]
def myenumerate(iterable, start=0):
    ...