from typing import Union
from typing import Iterable
from typing import Mapping
from typing import Callable
import json


def map_nested_objs(
    obj: Union[Iterable, Mapping], func: Callable
) -> Union[Iterable, Mapping]:
    """
    This function recursively applies `func` to each obj nested within a
    dictionary/array structure. Ex given ob [{"a": 1}, {"b": 2}], `func()`
    will be applied 1 and 2

    This function applies `func` in place and returns `ob`

    Args:
        obj: object to recursively run `func` on
        func: function to apply recursively
    Returns:
        The updated obj
    """
    if isinstance(obj, str):
        return map_nested_objs(json.loads(obj), func)
    elif isinstance(obj, Mapping):
        return {k: map_nested_objs(v, func) for k, v in obj.items()}
    elif isinstance(obj, Iterable):
        return [map_nested_objs(v, func) for v in obj]
    else:
        return func(obj)