Skip to content

Instantly share code, notes, and snippets.

@laundmo
Last active March 8, 2023 00:56
Show Gist options
  • Save laundmo/63b53691fcb71d232975444f3f8cf2b4 to your computer and use it in GitHub Desktop.
Save laundmo/63b53691fcb71d232975444f3f8cf2b4 to your computer and use it in GitHub Desktop.
Recursively get values from a dict/list structure by either key or value
"""
Copyright 2020
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Original Source: https://gist.github.com/laundmo/63b53691fcb71d232975444f3f8cf2b4
"""
def traverse(iterable, key=None, value=None, accessor=None):
"""Traverse a nested list/dict structure to find keys or values.
If just the key is passed, return a generator of all the values that correspond to that key.
If a value is passed, return a generator of all dicts which containe the value.
Will always return a tuple with the element and its accessor as a string.
The accessor uses the keys __repr__, which makes it usable without modification in simple cases.
Args:
iterable (list, dict): The nested list/dict structure.
key (hashable, optional): The key to search for. Defaults to None.
value (any, optional): The value to search for. Defaults to None.
accessor (dict, optional): Internally used to keep track of the key chain to get the returned element.
Yields:
tup (Tuple[any, str]): The value of the key, or the dictionary with the value and the chain of keys accessed to get it.
"""
if accessor is None:
accessor = ""
if not key and not value:
raise ValueError("Either key or value need to be passed")
if isinstance(iterable, list):
for i, local_value in enumerate(iterable):
yield from traverse(local_value, key=key, value=value, accessor=accessor + f"[{i}]")
elif isinstance(iterable, dict):
for local_key, local_value in iterable.items():
if local_key == key and not value:
yield local_value, accessor + f"[{repr(local_key)}]"
elif value and local_value == value:
yield iterable, accessor + f"[{repr(local_key)}]"
yield from traverse(local_value, key=key, value=value, accessor=accessor + f'[{repr(local_key)}]')
elif iterable == value:
yield iterable, accessor
if __name__== "__main__":
a = {"a":["a", "b", {"c": 5}], 2:{11:22}}
print(next(traverse(a, value=22)))
print(next(traverse(a, key="c")))
@Xithrius
Copy link

noice.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment