9.6. Iterator Map

  • Map (convert) elements in sequence

  • Generator (lazy evaluated)

  • map(callable, *iterables)

  • required callable - Function

  • required iterables - 1 or many sequence or iterator objects

>>> from inspect import isgeneratorfunction, isgenerator
>>>
>>>
>>> isgeneratorfunction(map)
False
>>>
>>> result = map(float, [1,2,3])
>>> isgenerator(result)
False

9.6.1. Example

>>> result = (float(x) for x in range(0,5))
>>>
>>> list(result)
[0.0, 1.0, 2.0, 3.0, 4.0]
>>> result = map(float, range(0,5))
>>>
>>> list(result)
[0.0, 1.0, 2.0, 3.0, 4.0]

9.6.2. Problem

>>> data = [1, 2, 3]
>>> result = []
>>>
>>> for x in data:
...     result.append(float(x))
>>>
>>> print(result)
[1.0, 2.0, 3.0]

9.6.3. Solution

>>> data = [1, 2, 3]
>>> result = map(float, data)
>>>
>>> list(result)
[1.0, 2.0, 3.0]

9.6.4. Lazy Evaluation

>>> data = [1, 2, 3]
>>> result = map(float, data)
>>>
>>> next(result)
1.0
>>> next(result)
2.0
>>> next(result)
3.0
>>> next(result)
Traceback (most recent call last):
StopIteration

9.6.5. Multi Parameters

>>> def myfunc(x):
...     return sum(x)
>>>
>>>
>>> DATA = [
...     (1,2),
...     (3,4),
... ]
>>>
>>> result = map(myfunc, DATA)
>>> print(list(result))
[3, 7]

9.6.6. Starmap

>>> from itertools import starmap
>>>
>>>
>>> DATA = [
...     (3.1415, 3),
...     (2.71828, 2),
... ]
>>>
>>> result = starmap(round, DATA)  # round(number=3.1415, ndigits=2)
>>> print(list(result))
[3.142, 2.72]

9.6.7. More Args

>>> numbers = [1.1111, 2.2222, 3.3333]
>>> prec = [1, 2, 3]
>>>
>>> result = map(round, numbers, prec)
>>> list(result)
[1.1, 2.22, 3.333]
  • round(1.1111, prec=1)

  • round(2.2222, prec=2)

  • round(3.3333, prec=3)

>>> numbers = [1.1111, 2.2222, 3.3333]
>>> prec = [1, 2, 3, 4]
>>>
>>> result = map(round, numbers, prec)
>>> list(result)
[1.1, 2.22, 3.333]
>>> numbers = [1.1111, 2.2222, 3.3333, 4.444]
>>> prec = [1, 2, 3]
>>>
>>> result = map(round, numbers, prec)
>>> list(result)
[1.1, 2.22, 3.333]

9.6.8. Performance

  • Date: 2024-08-29

  • Python: 3.12.4

  • IPython: 8.26.0

  • System: macOS 14.6.1

  • Computer: MacBook M3 Max

  • CPU: 16 cores (12 performance and 4 efficiency) / 3nm

  • RAM: 128 GB RAM LPDDR5

>>> def increment(x):
...     return x + 1
...
>>>
>>> data = [1, 2, 3, 4, 5]

Test 1:

>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = [increment(x) for x in data]
...
154 ns ± 37.6 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
152 ns ± 29.9 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
152 ns ± 36.9 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = map(increment, data)
...
56.8 ns ± 10.9 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
56.7 ns ± 11.5 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
58.4 ns ± 12.9 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = list(map(increment, data))
...
207 ns ± 33.7 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
209 ns ± 42.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
212 ns ± 42.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... result = list([increment(x) for x in data])
...
...
203 ns ± 40.3 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
200 ns ± 41.6 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
206 ns ± 47.3 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)

Test 2:

>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... for current in [increment(x) for x in data]:
...     pass
...
187 ns ± 33.4 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
189 ns ± 44.5 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
183 ns ± 29.3 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
>>> # doctest: +SKIP
... %%timeit -n 1000 -r 1000
... for current in map(increment, data):
...     pass
...
182 ns ± 36.2 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
184 ns ± 37.4 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)
182 ns ± 39.9 ns per loop (mean ± std. dev. of 1000 runs, 1,000 loops each)

9.6.9. Case Study

class Iris:
    def __init__(self, sl, sw, pl, pw, species):
        self.sl = sl
        self.sw = sw
        self.pl = pl
        self.pw = pw
        self.species = species

    def __repr__(self):
        return f'Iris({self.sl}, {self.sw}, {self.pl}, {self.pw}, {self.species})'


DATA = (5.8, 2.7, 5.1, 1.9, 'virginica')

# %%

result = Iris(*DATA)
print(result)
# Iris(5.8, 2.7, 5.1, 1.9, virginica)
from itertools import starmap
from pprint import pprint


class Iris:
    def __init__(self, sl, sw, pl, pw, species):
        self.sl = sl
        self.sw = sw
        self.pl = pl
        self.pw = pw
        self.species = species

    def __repr__(self):
        return f'Iris({self.sl}, {self.sw}, {self.pl}, {self.pw}, {self.species})'


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'),
]

header, *rows = DATA

# %%

result = [Iris(*row) for row in rows]
pprint(result)
# [Iris(5.8, 2.7, 5.1, 1.9, virginica),
#  Iris(5.1, 3.5, 1.4, 0.2, setosa),
#  Iris(5.7, 2.8, 4.1, 1.3, versicolor),
#  Iris(6.3, 2.9, 5.6, 1.8, virginica),
#  Iris(6.4, 3.2, 4.5, 1.5, versicolor),
#  Iris(4.7, 3.2, 1.3, 0.2, setosa),
#  Iris(7.0, 3.2, 4.7, 1.4, versicolor),
#  Iris(7.6, 3.0, 6.6, 2.1, virginica),
#  Iris(4.6, 3.1, 1.5, 0.2, setosa)]

# %%

result = starmap(Iris, rows)
pprint(list(result))
# [Iris(5.8, 2.7, 5.1, 1.9, virginica),
#  Iris(5.1, 3.5, 1.4, 0.2, setosa),
#  Iris(5.7, 2.8, 4.1, 1.3, versicolor),
#  Iris(6.3, 2.9, 5.6, 1.8, virginica),
#  Iris(6.4, 3.2, 4.5, 1.5, versicolor),
#  Iris(4.7, 3.2, 1.3, 0.2, setosa),
#  Iris(7.0, 3.2, 4.7, 1.4, versicolor),
#  Iris(7.6, 3.0, 6.6, 2.1, virginica),
#  Iris(4.6, 3.1, 1.5, 0.2, setosa)]

9.6.10. Use Case - 1

Built-in functions:

>>> DATA = [1, 2, 3]
>>> result = map(float, DATA)
>>>
>>> tuple(map(float, DATA))
(1.0, 2.0, 3.0)
>>> DATA = [1, 2, 3]
>>> result = map(float, DATA)
>>>
>>> set(map(float, DATA))
{1.0, 2.0, 3.0}
>>> DATA = [1, 2, 3]
>>> result = (float(x) for x in DATA)
>>>
>>> list(result)
[1.0, 2.0, 3.0]
>>> DATA = [1.1, 2.2, 3.3]
>>> result = map(round, DATA)
>>>
>>> list(result)
[1, 2, 3]

9.6.11. Use Case - 2

>>> def square(x):
...     return x ** 2
>>>
>>>
>>> DATA = [1, 2, 3]
>>> result = map(square, DATA)
>>>
>>> list(result)
[1, 4, 9]

9.6.12. Use Case - 3

>>> def increment(x):
...     return x + 1
>>>
>>>
>>> DATA = [1, 2, 3, 4]
>>> result = map(increment, DATA)
>>>
>>> list(result)
[2, 3, 4, 5]

9.6.13. Use Case - 4

>>> def translate(letter):
...     return PL.get(letter, letter)
>>>
>>>
>>> DATA = 'zażółć gęślą jaźń'
>>> PL = {'ą': 'a', 'ć': 'c', 'ę': 'e',
...       'ł': 'l', 'ń': 'n', 'ó': 'o',
...       'ś': 's', 'ż': 'z', 'ź': 'z'}
>>>
>>> result = map(translate, DATA)
>>> ''.join(result)
'zazolc gesla jazn'

9.6.14. Use Case - 5

Standard input:

>>> import sys
>>>
>>> 
... print(sum(map(int, sys.stdin)))
$ cat ~/.profile |grep addnum
alias addnum='python -c"import sys; print(sum(map(int, sys.stdin)))"'

9.6.15. Use Case - 6

>>> import requests
>>>
>>> url = 'https://python3.info/_static/iris-dirty.csv'
>>>
>>> data = requests.get(url).text
>>> header, *rows = data.splitlines()
>>> nrows, nfeatures, *class_labels = header.strip().split(',')
>>> label_encoder = dict(enumerate(class_labels))
>>> result = []
>>> for row in rows:
...     *features, species = row.strip().split(',')
...     features = map(float, features)
...     species = label_encoder[int(species)]
...     row = tuple(features) + (species,)
...     result.append(row)
>>> def decode(row):
...     *features, species = row.strip().split(',')
...     features = map(float, features)
...     species = label_encoder[int(species)]
...     return tuple(features) + (species,)
>>>
>>> result = map(decode, rows)
>>> def decode(row):
...     *features, species = row.strip().split(',')
...     features = map(float, features)
...     species = label_encoder[int(species)]
...     return tuple(features) + (species,)
>>>
>>> with open('/tmp/myfile.csv') as file:  
...     header = file.readline()
...     for line in map(decode, file):
...         print(line)

9.6.16. Use Case - 7

SetUp:

>>> from doctest import testmod as run_tests

Data [1]:

>>> DATA = """150,4,setosa,versicolor,virginica
... 5.1,3.5,1.4,0.2,0
... 7.0,3.2,4.7,1.4,1
... 6.3,3.3,6.0,2.5,2
... 4.9,3.0,1.4,0.2,0
... 6.4,3.2,4.5,1.5,1
... 5.8,2.7,5.1,1.9,2"""

Definition:

>>> def get_labelencoder(header: str) -> dict[int, str]:
...     """
...     >>> get_labelencoder('150,4,setosa,versicolor,virginica')
...     {0: 'setosa', 1: 'versicolor', 2: 'virginica'}
...     """
...     nrows, nfeatures, *class_labels = header.split(',')
...     return dict(enumerate(class_labels))
>>>
>>> run_tests()  
TestResults(failed=0, attempted=1)
>>> def get_data(line: str) -> tuple:
...     """
...     >>> convert('5.1,3.5,1.4,0.2,0')
...     (5.1, 3.5, 1.4, 0.2, 'setosa')
...     >>> convert('7.0,3.2,4.7,1.4,1')
...     (7.0, 3.2, 4.7, 1.4, 'versicolor')
...     >>> convert('6.3,3.3,6.0,2.5,2')
...     (6.3, 3.3, 6.0, 2.5, 'virginica')
...     """
...     *values, species = line.split(',')
...     values = map(float, values)
...     species = label_encoder[int(species)]
...     return tuple(values) + (species,)
>>>
>>> run_tests()  
TestResults(failed=0, attempted=3)
>>> header, *lines = DATA.splitlines()
>>> label_encoder = get_labelencoder(header)
>>> result = map(get_data, lines)
>>> list(result)  
[(5.1, 3.5, 1.4, 0.2, 'setosa'),
 (7.0, 3.2, 4.7, 1.4, 'versicolor'),
 (6.3, 3.3, 6.0, 2.5, 'virginica'),
 (4.9, 3.0, 1.4, 0.2, 'setosa'),
 (6.4, 3.2, 4.5, 1.5, 'versicolor'),
 (5.8, 2.7, 5.1, 1.9, 'virginica')]

9.6.17. Use Case - 8

>>> 
... import pandas as pd
...
...
... DATA = 'https://python3.info/_static/phones-pl.csv'
...
... result = (
...     pd
...     .read_csv(DATA, parse_dates=['datetime'])
...     .set_index('datetime', drop=True)
...     .drop(columns=['id'])
...     .loc['2000-01-01':'2000-03-01']
...     .query('item == "sms"')
...     .groupby(['period','item'])
...     .agg(
...         duration_count = ('duration', 'count'),
...         duration_sum = ('duration', 'sum'),
...         duration_median = ('duration', 'median'),
...         duration_mean = ('duration', 'mean'),
...         duration_std = ('duration', 'std'),
...         duration_var = ('duration', 'var'),
...         value = ('duration', lambda column: column.mean().astype(int))
...     )
... )

9.6.18. Use Case - 9

>>> from functools import reduce
>>> from operator import add
>>>
>>>
>>> def even(x):
...     return x % 2 == 0
>>>
>>> def positive(x):
...     return x > 0
>>>
>>> def non_negative(x):
...     return x >= 0
>>>
>>> def square(x):
...     return x ** 2
>>>
>>> def add1(x):
...     return x + 1
>>>
>>> def minus1(x):
...     return x + 1
>>> data = range(0, 1024)
>>> data = filter(even, data)
>>> data = filter(positive, data)
>>> data = filter(non_negative, data)
>>> data = map(square, data)
>>> data = map(add1, data)
>>> data = map(minus1, data)
>>> result = reduce(add, data)
>>>
>>> result
178434046
>>> filters = [
...     even,
...     positive,
...     non_negative,
... ]
>>>
>>> maps = [
...     square,
...     add1,
...     minus1,
... ]
>>>
>>> def apply(data, fn):
...     return map(fn, data)
>>>
>>>
>>> data = range(0, 1024)
>>> data = reduce(apply, filters, data)
>>> data = reduce(apply, maps, data)
>>> result = reduce(add, data)
>>>
>>> result
3072

9.6.19. Use Case - 10

>>> from pprint import pprint
>>> from dataclasses import dataclass
>>>
>>>
>>> DATA = """root:x:0:0:root:/root:/bin/bash
... bin:x:1:1:bin:/bin:/sbin/nologin
... daemon:x:2:2:daemon:/sbin:/sbin/nologin
... adm:x:3:4:adm:/var/adm:/sbin/nologin
... shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
... halt:x:7:0:halt:/sbin:/sbin/halt
... nobody:x:99:99:Nobody:/:/sbin/nologin
... sshd:x:74:74:Privilege-separated SSH:/var/empty/sshd:/sbin/nologin
... mwatney:x:1000:1000:Mark Watney:/home/mwatney:/bin/bash
... mlewis:x:1001:1001:Melissa Lewis:/home/mlewis:/bin/bash
... rmartinez:x:1002:1002:Rick Martinez:/home/rmartinez:/bin/bash
... avogel:x:1003:1003:Alex Vogel:/home/avogel:/bin/bash
... bjohanssen:x:1004:1004:Beth Johanssen:/home/bjohanssen:/bin/bash
... cbeck:x:1005:1005:Chris Beck:/home/cbeck:/bin/bash"""
>>>
>>>
>>> @dataclass
... class SystemAccount:
...     username: str
...     uid: int
>>>
>>> @dataclass
... class UserAccount:
...     username: str
...     uid: int
>>>
>>>
>>> def account(line):
...     records = line.strip().split(':')
...     username = records[0]
...     uid = int(records[2])
...     if uid < 1000:
...         return SystemAccount(username, uid)
...     else:
...         return UserAccount(username, uid)
>>>
>>> def system(account):
...     return account.uid < 1000
>>> all_accounts = map(account, DATA.splitlines())
>>> sys_accounts = filter(system, all_accounts)
>>> list(sys_accounts)  
[SystemAccount(username='root', uid=0),
 SystemAccount(username='bin', uid=1),
 SystemAccount(username='daemon', uid=2),
 SystemAccount(username='adm', uid=3),
 SystemAccount(username='shutdown', uid=6),
 SystemAccount(username='halt', uid=7),
 SystemAccount(username='nobody', uid=99),
 SystemAccount(username='sshd', uid=74)]

9.6.20. References

9.6.21. Assignments

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Float
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

# %% English
# 1. Use `map()` to apply function `float()` to DATA
# 2. Define `result: map` with result
# 3. Run doctests - all must succeed

# %% Polish
# 1. Użyj `map()` aby zaaplikować funkcję `float()` do DATA
# 2. Zdefiniuj `result: map` z wynikiem
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`
# - `float()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Evaluated `result` has invalid type, should be list'

>>> assert all(type(x) is float for x in result), \
'All rows in `result` should be float'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[1.0, 2.0, 3.0]
"""

DATA = [1, 2, 3]


# Use `map()` to apply function `float()` to DATA
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Apply
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% English
# 1. Define function `cube()`:
#    - takes one argument
#    - returns its argument cubed (raised to the power of 3)
# 2. Use `map()` to apply function `cube()` to DATA
# 3. Define `result: map` with result
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj funckję `cube()`:
#    - przyjmuje jeden argument
#    - zwraca argument podniesiony do sześcianu (do 3 potęgi)
# 2. Użyj `map()` aby zaaplikować funkcję `cube()` do DATA
# 3. Zdefiniuj `result: map` z wynikiem
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`
# - `pow()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction
>>> assert isfunction(cube), \
'Object `cube` must be a function'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Evaluated `result` has invalid type, should be list'

>>> assert all(type(x) is int for x in result), \
'All rows in `result` should be int'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[0, 1, 8, 27, 64, 125, 216, 343, 512, 729]
"""

DATA = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

# Returns its argument cubed (raised to the power of 3)
# type: Callable[[int], [int]]
def cube(x):
    ...

# Cube numbers in DATA
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Apply
# - Difficulty: easy
# - Lines: 3
# - Minutes: 3

# %% English
# 1. Define function `upper()`:
#    - takes one argument (str)
#    - returns argument uppercased
# 2. Use `map()` to apply function `upper()` to DATA
# 3. Define `result: map` with result
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj funckję `upper()`:
#    - przyjmuje jeden argument (str)
#    - zwraca argument dużymi literami
# 2. Użyj `map()` aby zaaplikować funkcję `upper()` do DATA
# 3. Zdefiniuj `result: map` z wynikiem
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`
# - `str.upper()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction
>>> assert isfunction(upper), \
'Object `upper` must be a function'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Evaluated `result` has invalid type, should be list'

>>> assert all(type(x) is str for x in result), \
'All rows in `result` should be str'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
['A', 'B', 'C']
"""

DATA = ['a', 'b', 'c']

# Define function `upper()`:
# - takes one argument (str)
# - returns argument uppercased
# type: Callable[[str], [str]]
def upper():
    ...

# Use `map()` to apply function `upper()` to DATA
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Apply
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

# %% English
# 1. Use `map()` to apply function `str.upper()` to DATA
# 2. Do not define own function
# 3. Define `result: map` with result
# 4. Run doctests - all must succeed

# %% Polish
# 1. Użyj `map()` aby zaaplikować funkcję `str.upper()` do DATA
# 2. Nie definiuj własnej funkcji
# 3. Zdefiniuj `result: map` z wynikiem
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`
# - `str.upper()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Evaluated `result` has invalid type, should be list'

>>> assert all(type(x) is str for x in result), \
'All rows in `result` should be str'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
['A', 'B', 'C']
"""

DATA = ['a', 'b', 'c']

# Use `map()` to apply function `str.upper()` to DATA
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map FromISOFormat
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

# %% English
# 1. Define `result: map` with parsed `DATA` dates
# 2. Use `map()` and `datetime.fromisoformat()`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj `result: map` ze sparsowanymi datami `DATA`
# 2. Użyj `map()` oraz `datetime.fromisoformat()`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`
# - `datetime.fromisoformat()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

>>> assert type(result) is map, \
'Variable `result` has invalid type, must be a map'

>>> result = list(result)
>>> assert type(result) is list, \
'Variable `result` has invalid type, must be a list'

>>> assert all(type(element) is datetime for element in result), \
'All elements in `result` must be a datetime'

>>> from pprint import pprint
>>> pprint(result, width=30, sort_dicts=False)
[datetime.datetime(1961, 4, 12, 6, 7),
 datetime.datetime(1961, 4, 12, 6, 7)]
"""

from datetime import datetime

DATA = [
    '1961-04-12 06:07',
    '1961-04-12 06:07:00',
]

# Define `result: map` with parsed `DATA` dates
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Login
# - Difficulty: medium
# - Lines: 1
# - Minutes: 2

# %% English
# 1. Define `result: map` with logged users from `DATA`
# 2. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj `result: map` z zalogowanymi użytkownikami z `DATA`
# 2. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`
# - `class.method(instance)`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Variable `result` has invalid type, must be a list'

>>> assert all(type(element) is str for element in result), \
'All elements in `result` must be a str'

>>> from pprint import pprint
>>> pprint(result, width=30, sort_dicts=False)
['User login: mwatney',
 'User login: mlewis']
"""

class User:
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def login(self):
        return f'User login: {self.username}'


USERS = [
    User('mwatney', 'Ares3'),
    User('mlewis', 'Nasa69'),
]

result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Logs
# - Difficulty: medium
# - Lines: 7
# - Minutes: 8

# %% English
# 1. Define `parse()` to extract date, time, level and message
# 2. Define `result: map` with `parse()` function applied to `DATA`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj `parse()` do wyciągnięcia dat, czasu, poziomu i wiadomości
# 2. Zdefiniuj `result: map` z funkcją `parse() zaaplikowaną do danych
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% References
# [1] National Aeronautics and Space Administration.
#     Apollo 11 timeline.
#     Year: 1969. Retrieved: 2021-03-25.
#     URL: https://history.nasa.gov/SP-4029/Apollo_11i_Timeline.htm

# %% Hints
# - Note, that last time has no seconds
# - This is not bug, time without seconds is in NASA history records [1]
# - `str.splitlines()`
# - `str.split(', ', maxsplit=3)`
# - `date.fromisoformat()`
# - `time.fromisoformat()`
# - `datetime.combine()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Variable `result` has invalid type, must be a list'

>>> assert all(type(row) is dict for row in result), \
'All elements in result must be dict'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[{'datetime': datetime.datetime(1969, 7, 14, 21, 0),
  'level': 'INFO',
  'message': 'Terminal countdown started'},
 {'datetime': datetime.datetime(1969, 7, 16, 13, 31, 53),
  'level': 'WARNING',
  'message': 'S-IC engine ignition (#5)'},
 {'datetime': datetime.datetime(1969, 7, 16, 13, 33, 23),
  'level': 'DEBUG',
  'message': 'Maximum dynamic pressure (735.17 lb/ft^2)'},
 {'datetime': datetime.datetime(1969, 7, 16, 13, 34, 44),
  'level': 'WARNING',
  'message': 'S-II ignition'},
 {'datetime': datetime.datetime(1969, 7, 16, 13, 35, 17),
  'level': 'DEBUG',
  'message': 'Launch escape tower jettisoned'},
 {'datetime': datetime.datetime(1969, 7, 16, 13, 39, 40),
  'level': 'DEBUG',
  'message': 'S-II center engine cutoff'},
 {'datetime': datetime.datetime(1969, 7, 16, 16, 22, 13),
  'level': 'INFO',
  'message': 'Translunar injection'},
 {'datetime': datetime.datetime(1969, 7, 16, 16, 56, 3),
  'level': 'INFO',
  'message': 'CSM docked with LM/S-IVB'},
 {'datetime': datetime.datetime(1969, 7, 16, 17, 21, 50),
  'level': 'INFO',
  'message': 'Lunar orbit insertion ignition'},
 {'datetime': datetime.datetime(1969, 7, 16, 21, 43, 36),
  'level': 'INFO',
  'message': 'Lunar orbit circularization ignition'},
 {'datetime': datetime.datetime(1969, 7, 20, 17, 44),
  'level': 'INFO',
  'message': 'CSM/LM undocked'},
 {'datetime': datetime.datetime(1969, 7, 20, 20, 5, 5),
  'level': 'WARNING',
  'message': 'LM powered descent engine ignition'},
 {'datetime': datetime.datetime(1969, 7, 20, 20, 10, 22),
  'level': 'ERROR',
  'message': 'LM 1202 alarm'},
 {'datetime': datetime.datetime(1969, 7, 20, 20, 14, 18),
  'level': 'ERROR',
  'message': 'LM 1201 alarm'},
 {'datetime': datetime.datetime(1969, 7, 20, 20, 17, 39),
  'level': 'WARNING',
  'message': 'LM lunar landing'},
 {'datetime': datetime.datetime(1969, 7, 21, 2, 39, 33),
  'level': 'DEBUG',
  'message': 'EVA started (hatch open)'},
 {'datetime': datetime.datetime(1969, 7, 21, 2, 56, 15),
  'level': 'WARNING',
  'message': '1st step taken lunar surface (CDR)'},
 {'datetime': datetime.datetime(1969, 7, 21, 2, 56, 15),
  'level': 'WARNING',
  'message': 'Neil Armstrong first words on the Moon'},
 {'datetime': datetime.datetime(1969, 7, 21, 3, 5, 58),
  'level': 'DEBUG',
  'message': 'Contingency sample collection started (CDR)'},
 {'datetime': datetime.datetime(1969, 7, 21, 3, 15, 16),
  'level': 'INFO',
  'message': 'LMP on lunar surface'},
 {'datetime': datetime.datetime(1969, 7, 21, 5, 11, 13),
  'level': 'DEBUG',
  'message': 'EVA ended (hatch closed)'},
 {'datetime': datetime.datetime(1969, 7, 21, 17, 54),
  'level': 'WARNING',
  'message': 'LM lunar liftoff ignition (LM APS)'},
 {'datetime': datetime.datetime(1969, 7, 21, 21, 35),
  'level': 'INFO',
  'message': 'CSM/LM docked'},
 {'datetime': datetime.datetime(1969, 7, 22, 4, 55, 42),
  'level': 'WARNING',
  'message': 'Transearth injection ignition (SPS)'},
 {'datetime': datetime.datetime(1969, 7, 24, 16, 21, 12),
  'level': 'INFO',
  'message': 'CM/SM separation'},
 {'datetime': datetime.datetime(1969, 7, 24, 16, 35, 5),
  'level': 'WARNING',
  'message': 'Entry'},
 {'datetime': datetime.datetime(1969, 7, 24, 16, 50, 35),
  'level': 'WARNING',
  'message': 'Splashdown (went to apex-down)'},
 {'datetime': datetime.datetime(1969, 7, 24, 17, 29),
  'level': 'INFO',
  'message': 'Crew egress'}]
"""
from datetime import date, datetime, time

DATA = """1969-07-14, 21:00:00, INFO, Terminal countdown started
1969-07-16, 13:31:53, WARNING, S-IC engine ignition (#5)
1969-07-16, 13:33:23, DEBUG, Maximum dynamic pressure (735.17 lb/ft^2)
1969-07-16, 13:34:44, WARNING, S-II ignition
1969-07-16, 13:35:17, DEBUG, Launch escape tower jettisoned
1969-07-16, 13:39:40, DEBUG, S-II center engine cutoff
1969-07-16, 16:22:13, INFO, Translunar injection
1969-07-16, 16:56:03, INFO, CSM docked with LM/S-IVB
1969-07-16, 17:21:50, INFO, Lunar orbit insertion ignition
1969-07-16, 21:43:36, INFO, Lunar orbit circularization ignition
1969-07-20, 17:44:00, INFO, CSM/LM undocked
1969-07-20, 20:05:05, WARNING, LM powered descent engine ignition
1969-07-20, 20:10:22, ERROR, LM 1202 alarm
1969-07-20, 20:14:18, ERROR, LM 1201 alarm
1969-07-20, 20:17:39, WARNING, LM lunar landing
1969-07-21, 02:39:33, DEBUG, EVA started (hatch open)
1969-07-21, 02:56:15, WARNING, 1st step taken lunar surface (CDR)
1969-07-21, 02:56:15, WARNING, Neil Armstrong first words on the Moon
1969-07-21, 03:05:58, DEBUG, Contingency sample collection started (CDR)
1969-07-21, 03:15:16, INFO, LMP on lunar surface
1969-07-21, 05:11:13, DEBUG, EVA ended (hatch closed)
1969-07-21, 17:54:00, WARNING, LM lunar liftoff ignition (LM APS)
1969-07-21, 21:35:00, INFO, CSM/LM docked
1969-07-22, 04:55:42, WARNING, Transearth injection ignition (SPS)
1969-07-24, 16:21:12, INFO, CM/SM separation
1969-07-24, 16:35:05, WARNING, Entry
1969-07-24, 16:50:35, WARNING, Splashdown (went to apex-down)
1969-07-24, 17:29, INFO, Crew egress"""


# Define `parse()` to extract date, time, level and message
# type: Callable[[str], [Log]]
def parse(line):
    ...

# Define `result: map` with `parse()` function applied to `DATA`
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map CSV
# - Difficulty: easy
# - Lines: 5
# - Minutes: 5

# %% English
# 1. Define function `parse(str) -> list[tuple]`
# 2. Define `result: map` with function `parse()` applied to `DATA`
# 3. Convert numeric values to `float`
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj funkcję `parse(str) -> list[tuple]`
# 2. Zdefiniuj `result: map` z funkcją `parse()` zaaplikowaną do `DATA`
# 3. Przekonwertuj wartości numeryczne do `float`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `str.strip()`
# - `str.split()`
# - `str.splitlines()`
# - `map()`
# - `float()`
# - `('hello',)` - one element tuple
# - `(1, 2, 3) + ('hello',)` - adding tuples

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)  # expand map object
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'

>>> assert all(type(x) is tuple for x in result), \
'All rows in `result` should be tuple'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[(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')]
"""

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


# values from file (note the tuple format!)
# type: Callable[[str], [tuple]]
def parse(line):
    ...

# Define `result: map` with function `parse()` applied to `DATA`
# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map ReadTypeCast
# - Difficulty: easy
# - Lines: 6
# - Minutes: 8

# %% English
# 1. Define `class_labels: list[str]` from header (species names)
# 2. Define `label_encoder: dict[int,str]` converting `class_labels`
#    (species name index is the last digit in the line)
# 3. Define function `parse(str) -> list[tuple]`
# 4. Define `result: map` with function `parse()` applied to `DATA`
# 5. Convert numeric values to `float`
# 6. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj `class_labels: list[str]` z nagłówka (nazwy gatunków)
# 2. Zdefiniuj `label_encoder: dict[int,str]` przekształcając `class_labels`
#    (indeks nazwy gatunku to ostatnia cyfra w linii)
# 3. Zdefiniuj funckję `parse(str) -> list[tuple]`
# 4. Zdefiniuj `result: map` z funkcją `parse()` zaaplikowaną do `DATA`
# 5. Przekonwertuj wartości numeryczne do `float`
# 6. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `str.splitlines()`
# - `str.strip()`
# - `str.split()`
# - `result, *others = 1, 2, 3, 4`
# - `dict()`
# - `enumerate()`
# - `map()`
# - `float()`
# - `('hello',)` - one element tuple
# - `(1, 2, 3) + ('hello',)` - adding tuples

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)  # expand map object
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'

>>> assert all(type(x) is tuple for x in result), \
'All rows in `result` should be tuple'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[(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')]
"""

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

header, *lines = DATA.splitlines()
nrows, nfeatures, *class_labels = header.strip().split(',')
label_encoder = dict(enumerate(class_labels))

# type: Callable[[str], [tuple]]
def parse(line):
    ...

# type: map
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: Iterator Map Parse
# - Difficulty: easy
# - Lines: 7
# - Minutes: 8

# %% English
# 1. Use `map()` to apply function `convert()` to DATA
# 2. Function `convert()`:
#    - Takes string
#    - Splits string
#    - Returns `dict` with `ip` and `hosts` as keys, example:
#        {'ip': '10.13.37.1', 'hosts': ['nasa.gov', 'esa.int']}
# 2. Define `result: map` with result
# 3. Run doctests - all must succeed

# %% Polish
# 1. Użyj `map()` aby zaaplikować funkcję `convert()` do DATA
# 2. Funkcja `convert()`:
#    - Przyjmuje stringa
#    - Dzieli stringa
#    - Zwraca `dict` z `ip` i `hosts` jako klucze, przykład:
#        {'ip': '10.13.37.1', 'hosts': ['nasa.gov', 'esa.int']}
# 2. Zdefiniuj `result: map` z wynikiem
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `map()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isfunction
>>> assert isfunction(convert), \
'Object `parse` must be a function'

>>> assert result is not Ellipsis, \
'Assign result to variable: `result`'

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

>>> result = list(result)
>>> assert type(result) is list, \
'Evaluated `result` has invalid type, should be list'

>>> assert all(type(x) is dict for x in result), \
'All rows in `result` should be dict'

>>> from pprint import pprint
>>> pprint(result, width=72, sort_dicts=False)
[{'ip': '127.0.0.1', 'hosts': ['localhost']},
 {'ip': '127.0.0.1', 'hosts': ['astromatt']},
 {'ip': '10.13.37.1', 'hosts': ['nasa.gov', 'esa.int']},
 {'ip': '255.255.255.255', 'hosts': ['broadcasthost']},
 {'ip': '::1', 'hosts': ['localhost']}]
"""

DATA = """127.0.0.1       localhost
127.0.0.1       astromatt
10.13.37.1      nasa.gov esa.int
255.255.255.255 broadcasthost
::1             localhost"""

def convert(line):
    ...

result = ...