10.3. FP Apply Reduce

10.3.1. About

  • Reduce sequence using function

  • Built-in

In Python, reduce() is a built-in function that applies a given function to the elements of an iterable (e.g. a list, tuple, or set) and returns a single value. The function takes two arguments: the first is the function to apply, and the second is the iterable to be processed.

The reduce() function works by applying the function to the first two elements of the iterable, then to the result and the next element, and so on, until all elements have been processed and a single value is obtained.

../../_images/fp-reduce.png

10.3.2. Example

Here's an example:

>>> from functools import reduce
>>>
>>> # Define a list of numbers
>>> numbers = [1, 2, 3, 4, 5]
>>>
>>> # Use reduce() to sum the numbers
>>> result = reduce(lambda x,y: x+y, numbers)
>>>
>>> print(result)
15

In this example, the reduce() function applies the lambda function (which adds two numbers) to the elements of the numbers list, resulting in the sum of all the numbers.

10.3.3. SetUp

>>> from functools import reduce

10.3.4. Problem

>>> def add(x, y):
...     return x + y
>>>
>>>
>>> DATA = [1, 2, 3, 4]
>>> result = 0
>>>
>>> for element in DATA:
...     result = add(result, element)
>>>
>>> print(result)
10

10.3.5. Solution

>>> DATA = [1, 2, 3, 4]
>>>
>>>
>>> def add(x, y):
...     return x + y
>>>
>>> reduce(add, DATA)
10

10.3.6. Rationale

The operator module in Python provides a set of functions that implement common operations on Python objects, such as arithmetic operations, comparisons, and logical operations. These functions are designed to be used as functional arguments to other functions, such as map(), filter(), and reduce().

The operator module provides functions for arithmetic operations such as addition, subtraction, multiplication, division, and exponentiation. It also provides functions for bitwise operations, such as bitwise AND, OR, XOR, and shift operations.

In addition to arithmetic and bitwise operations, the operator module provides functions for comparisons, such as greater than, less than, equal to, and not equal to. It also provides functions for logical operations, such as not, and, and or.

Here's an example of using the operator module to sort a list of tuples based on the second element of each tuple:

>>> import operator
>>>
>>> # Define a list of tuples
>>> data = [(2, 'b'), (1, 'a'), (3, 'c')]
>>>
>>> # Sort the list based on the second element of each tuple
>>> sorted_data = sorted(data, key=operator.itemgetter(1))
>>>
>>> print(sorted_data)
[(1, 'a'), (2, 'b'), (3, 'c')]

In this example, the itemgetter() function from the operator module is used as the key argument to the sorted() function. This function returns a callable that extracts the second element of each tuple, which is used to sort the list.

>>> DATA = [1, 2, 3, 4]
>>>
>>> from operator import mul
>>> reduce(mul, DATA)
24
>>> def add(a, b):
...     print(f'{a=}, {b=}')
...     return a + b
>>>
>>>
>>> data = [1, 2, 3, 4]
>>> reduce(add, data)
a=1, b=2
a=3, b=3
a=6, b=4
10

10.3.7. Use Case - 1

>>> from functools import reduce
>>>
>>>
>>> def square(x):
...     return x ** 2
>>>
>>> def cube(x):
...     return x ** 3
>>>
>>> def increment(x):
...     return x + 1
>>>
>>> def decrement(x):
...     return x - 1
>>>
>>> def even(x):
...     return x % 2 == 0
>>>
>>> def gt10(x):
...     return x > 10
>>>
>>> def add(a, b):
...     return a + b
>>>
>>>
>>> data = (1, 2, 3, 4, 5)
>>>
>>> incremented = map(increment, data)
>>> squared = map(square, incremented)
>>> filtered = filter(even, squared)
>>> decremented = map(decrement, filtered)
>>> final = filter(gt10, decremented)
>>> result = reduce(add, final)
>>>
>>> print(result)
50

10.3.8. Use Case - 2

>>> from functools import reduce
>>>
>>>
>>> def add(x, y):
...     return x + y
>>>
>>> def sub(x, y):
...     return x - y
>>>
>>> def mul(x, y):
...     return x * y
>>>
>>>
>>> data = (1, 2, 3, 4)
>>>
>>> reduce(add, data)
10
>>> reduce(sub, data)
-8
>>> reduce(mul, data)
24

10.3.9. Use Case - 3

>>> from functools import reduce
>>>
>>>
>>> data = (1, 2, 3, 4)
>>>
>>> reduce(lambda x,y: x+y, data)
10
>>> reduce(lambda x,y: x-y, data)
-8
>>> reduce(lambda x,y: x*y, data)
24

10.3.10. Use Case - 4

>>> from functools import reduce
>>> from operator import add, sub, mul
>>>
>>>
>>> data = (1, 2, 3, 4)
>>>
>>> reduce(add, data)
10
>>> reduce(sub, data)
-8
>>> reduce(mul, data)
24

10.3.11. Use Case - 5

Iterative approach:

>>> data = [
...     [1, 2, 3],
...     [4, 5, 6],
...     [7, 8, 9],
... ]
>>>
>>> result = 0
>>>
>>> for row in data:
...     for digit in row:
...         result += digit
>>>
>>> print(result)
45

Functional approach:

>>> from functools import reduce
>>> from operator import add
>>>
>>>
>>> data = [
...     [1, 2, 3],
...     [4, 5, 6],
...     [7, 8, 9],
... ]
>>>
>>> result = reduce(add, (
...     reduce(add, data[0]),
...     reduce(add, data[1]),
...     reduce(add, data[2]),
... ))
>>>
>>> print(result)
45

10.3.12. Use Case - 6

>>> from functools import reduce
>>> from itertools import starmap
>>> from operator import add, sub, mul
>>> def square(x):
...     return x ** 2
>>>
>>> def cube(x):
...     return x ** 3
>>>
>>> def apply(data, fn):
...     return map(fn, data)
>>> data = (1, 2, 3, 4)
>>> funcs = (square, cube)
>>>
>>> result = reduce(apply, funcs, data)
>>> tuple(result)
(1, 64, 729, 4096)
>>>
>>> result = reduce(apply, funcs, data)
>>> reduce(add, result)
4890
>>> data = (1, 2, 3, 4)
>>> funcs = (add, sub, mul)
>>>
>>> result = map(lambda fn: reduce(fn,data), funcs)
>>> reduce(add, result)
26
>>> funcs = (
...     (add, data),
...     (sub, data),
...     (mul, data),
... )
>>>
>>> data = (1, 2, 3, 4)
>>> result = starmap(reduce, funcs)
>>> reduce(add, result)
26
>>> result = starmap(reduce, (
...     (add, data),
...     (sub, data),
...     (mul, data),
... ))
>>>
>>> data = (1, 2, 3, 4)
>>> reduce(add, result)
26

10.3.13. Use Case - 7

  • split-apply-combine strategy

Apply function of two arguments cumulatively to the items of iterable, from left to right, so as to reduce the iterable to a single value. For example, reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) calculates ((((1+2)+3)+4)+5). The left argument, x, is the accumulated value and the right argument, y, is the update value from the iterable. If the optional initializer is present, it is placed before the items of the iterable in the calculation, and serves as a default when the iterable is empty. If initializer is not given and iterable contains only one item, the first item is returned.

Roughly equivalent to:

>>> def reduce(function, iterable, initializer=None):
...     it = iter(iterable)
...     if initializer is None:
...         value = next(it)
...     else:
...         value = initializer
...     for element in it:
...         value = function(value, element)
...     return value

SetUp:

>>> from functools import reduce
>>>
>>> DATA = (1, 2, 3, 4, 5)

Usage:

>>> def add(a, b):
...     return a + b
>>>
>>> reduce(add, DATA)
15
>>> reduce(lambda x,y: x+y, DATA)
15

10.3.14. Use Case - 8

>>> from functools import reduce
>>>
>>>
>>> def square(x):
...     return x ** 2
>>>
>>> def cube(x):
...     return x ** 3
>>>
>>> def apply(data, fn):
...     return map(fn, data)
>>>
>>> def add(x, y):
...     return x + y
>>>
>>>
>>> data = (1, 2, 3, 4)
>>> transformations = (square, cube)
>>>
>>> transformed = reduce(apply, transformations, data)  # (1, 64, 729, 4096)
>>> result = reduce(add, transformed)
>>>
>>> print(result)
4890

10.3.15. Assignments

# %% About
# - Name: Functional Reduce DefAdd
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

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

# %% English
# 1. Reduce `DATA` to calculate sum of its elements
# 2. Define function `add(x: int, y: int) -> int` that returns sum of `x` and `y`
# 3. Use `functools.reduce()`
# 4. Define variable `result` with the solution
# 5. Run doctests - all must succeed

# %% Polish
# 1. Zredukuj `DATA` aby obliczyć sumę jego elementów
# 2. Zdefiniuj funkcję `add(x: int, y: int) -> int`, która zwraca sumę `x` i `y`
# 3. Użyj `functools.reduce()`
# 4. Zdefiniuj zmienną `result` z rozwiązaniem
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `reduce(callable, iterable)`
# - `def myfunc(): ...`

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0

>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'

>>> from inspect import isfunction

>>> assert 'add' in globals(), \
'Function `add` is not defined; define it before using.'

>>> assert isfunction(add), \
'Object `add` has an invalid type; expected `function`.'

>>> assert 'result' in globals(), \
'Variable `result` is not defined; assign result of your program to it.'

>>> assert result is not Ellipsis, \
'Variable `result` has an invalid value; assign result of your program to it.'

>>> assert type(result) is int, \
'Variable `result` has an invalid type; expected: `int`.'

>>> result
15
"""

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

# %% Imports
from functools import reduce

# %% Types
result: int

# %% Data
def add(x, y):
    return x + y


DATA = (1, 2, 3, 4, 5)

# %% Result
result = ...

# %% About
# - Name: Functional Reduce OperatorAdd
# - Difficulty: easy
# - Lines: 1
# - Minutes: 2

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

# %% English
# 1. Reduce `DATA` to calculate sum of its elements
# 2. Use `operator.add()`
# 3. Use `functools.reduce()`
# 4. Define variable `result` with the solution
# 5. Run doctests - all must succeed

# %% Polish
# 1. Zredukuj `DATA` używając funkcji `add()` aby obliczyć sumę jego elementów
# 4. Użyj `operator.add()`
# 3. Użyj `functools.reduce()`
# 4. Zdefiniuj zmienną `result` z rozwiązaniem
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `reduce(callable, iterable)`
# - `operator.add()`

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0

>>> assert sys.version_info >= (3, 9), \
'Python has an is invalid version; expected: `3.9` or newer.'

>>> from inspect import isfunction

>>> assert 'add' in globals(), \
'Function `add` is not defined; define it before using.'

>>> assert isfunction(add), \
'Object `add` has an invalid type; expected `function`.'

>>> assert 'result' in globals(), \
'Variable `result` is not defined; assign result of your program to it.'

>>> assert result is not Ellipsis, \
'Variable `result` has an invalid value; assign result of your program to it.'

>>> assert type(result) is int, \
'Variable `result` has an invalid type; expected: `int`.'

>>> result
15
"""

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

# %% Imports
from functools import reduce

# %% Types
result: int

# %% Data
def add(x, y):
    return x + y


DATA = (1, 2, 3, 4, 5)

# %% Result
result = ...