Created
April 30, 2021 16:39
-
-
Save tommct/17859f5f3f2f56f9fd9ea0ce7fec5566 to your computer and use it in GitHub Desktop.
Walk a collection, like a JSON object, using a callback.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import logging | |
from collections.abc import Iterable | |
def is_container(obj): | |
return isinstance(obj, Iterable) and not isinstance(obj, (str, bytes, bytearray)) | |
# https://stackoverflow.com/a/54000999/394430 | |
def walk_collection(obj, callback=None, _path: list=[], **kwargs): | |
"""Walk an arbitrarily nested structure of lists and/or dicts such as would be made when | |
reading JSON as an object. Walking is performed in a depth-first search manner. | |
Args: | |
obj (dict, list, set, tuple): Container object, possibly nested. | |
callback (function, optional): If specified, this function gets called on each node | |
when the algorithm has arrived at it. It must accept at least two first arguments: | |
the current node, and the path to that node. Defaults to None. | |
kwargs (dict, optional): If `callback` is used, this permits kwargs to be passed to | |
that function. Defaults to None. | |
""" | |
if isinstance(obj, dict): | |
if callback: | |
callback(obj, _path, **kwargs) | |
for k,v in obj.items(): | |
if is_container(v) is False: | |
if callback: | |
_path.append(k) | |
callback(v, _path, **kwargs) | |
_path.pop() | |
elif isinstance(v, list) or isinstance(v, set): | |
_path.append(k) | |
if callback: | |
callback(v, _path, **kwargs) | |
for elem in v: | |
walk_collection(elem, callback, _path, **kwargs) | |
_path.pop() | |
elif isinstance(v, dict): | |
_path.append(k) | |
walk_collection(v, callback, _path, **kwargs) | |
_path.pop() | |
else: | |
logging.warning(f'Type "{type(v).__name__}" not recognized: {".".join(_path)}. key={k}, val={v}') | |
elif isinstance(obj, list): | |
if callback: | |
callback(obj, _path, **kwargs) | |
for elem in obj: | |
walk_collection(elem, callback, _path, **kwargs) | |
elif callback and is_container(obj) is False: | |
callback(obj, _path, **kwargs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment