I have an arbitrarily nested iterable like so:
numbers = (1, 2, (3, (4, 5)), 7)
and I'd like to map a function over it without changing the structure. For example, I might want to convert all the numbers to strings to get
strings = recursive_map(str, numbers)
assert strings == ('1', '2', ('3', ('4', '5')), '7')
Is there a nice way to do this? I can imaging writing my own method to manually traverse numbers
, but I'd like to know if there's a general way to map over recursive iterables.
Also, in my example, it's okay if strings
gives me nested lists (or some iterable) rather nested tuples.
map() applies function to each item in iterable in a loop and returns a new iterator that yields transformed items on demand. function can be any Python function that takes a number of arguments equal to the number of iterables you pass to map() .
In Python, you can use map() to apply built-in functions, lambda expressions ( lambda ), functions defined with def , etc., to all items of iterables such as lists and tuples.
map() works way faster than for loop. Considering the same code above when run in this ide. Using map():
You can use the Python map() function with a String. While using the map() with a String, the latter will act like an array. You can then use the map() function as an iterator to iterate through all the string characters.
We scan every element in the sequence, and proceeds into deeper recursion if the current item is a sub-sequence, or yields it's mapping if we reached a non-sequence data type (could be int
, str
, or any complex classes).
We use collections.Sequence
to generalize the idea for every sequence, and not only tuples or lists, and type(item)
upon yield to ensure that the sub-sequences we get back remains of the same type they were.
from collections import Sequence
def recursive_map (seq, func):
for item in seq:
if isinstance(item, Sequence):
yield type(item)(recursive_map(item, func))
else:
yield func(item)
Demo:
>>> numbers = (1, 2, (3, (4, 5)), 7)
>>> mapped = recursive_map(numbers, str)
>>> tuple(mapped)
('1', '2', ('3', ('4', '5')), '7')
Or a more complex example:
>>> complex_list = (1, 2, [3, (complex('4+2j'), 5)], map(str, (range(7, 10))))
>>> tuple(recursive_map(complex_list, lambda x: x.__class__.__name__))
('int', 'int', ['int', ('complex', 'int')], 'map')
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With