Skip to content

Instantly share code, notes, and snippets.

@tommct
Created April 30, 2021 16:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tommct/17859f5f3f2f56f9fd9ea0ce7fec5566 to your computer and use it in GitHub Desktop.
Save tommct/17859f5f3f2f56f9fd9ea0ce7fec5566 to your computer and use it in GitHub Desktop.
Walk a collection, like a JSON object, using a callback.
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