"""
Overview:
Function operations for nested structure.
This module provides utilities for applying functions to nested data structures
(lists, tuples, and dictionaries) in a recursive manner. It allows mapping operations
over complex nested structures while preserving their original types and hierarchy.
"""
from ..reflection import dynamic_call
__all__ = [
'nested_map'
]
[docs]
def nested_map(f, s):
"""
Map the nested structure with a function.
This function recursively traverses a nested structure (containing lists, tuples,
and dictionaries) and applies the given function to each leaf value. The function
can optionally accept the path to the current element as a parameter.
:param f: The function to apply to each leaf value. Can accept 0, 1, or 2 parameters:
- 0 params: Returns a constant value
- 1 param: Receives the leaf value
- 2 params: Receives the leaf value and its path (tuple of keys/indices)
:type f: callable
:param s: The nested structure to map over. Can be a dict, list, tuple, or any
combination thereof, with leaf values of any type.
:type s: dict or list or tuple or any
:return: A new nested structure with the same type and hierarchy as the input,
but with the function applied to all leaf values.
:rtype: Same type as input structure
Examples::
>>> from hbutils.collection import nested_map
>>> nested_map(lambda x: x + 1, [
... 2, 3, (4, {'x': 2, 'y': 4}),
... {'a': 3, 'b': (4, 5)},
... ])
[3, 4, (5, {'x': 3, 'y': 5}), {'a': 4, 'b': (5, 6)}]
>>> nested_map(lambda x, p: (x + 1) * len(p), [
... 2, 3, (4, {'x': 2, 'y': 4}),
... {'a': 3, 'b': (4, 5)},
... ])
[3, 4, (10, {'x': 9, 'y': 15}), {'a': 8, 'b': (15, 18)}]
>>> nested_map(lambda: 233, [
... 2, 3, (4, {'x': 2, 'y': 4}),
... {'a': 3, 'b': (4, 5)},
... ])
[233, 233, (233, {'x': 233, 'y': 233}), {'a': 233, 'b': (233, 233)}]
"""
_df = dynamic_call(f)
def _recursion(sval, p):
"""
Recursively traverse and map the nested structure.
:param sval: The current value being processed
:type sval: any
:param p: The path to the current value (tuple of keys/indices)
:type p: tuple
:return: The mapped value or structure
:rtype: any
"""
if isinstance(sval, dict):
return type(sval)({k: _recursion(v, (*p, k)) for k, v in sval.items()})
elif isinstance(sval, (list, tuple)):
return type(sval)(_recursion(v, (*p, i)) for i, v in enumerate(sval))
else:
return _df(sval, p)
return _recursion(s, ())