Skip to content

Commit

Permalink
Merge pull request #115 from sciris/search-objects
Browse files Browse the repository at this point in the history
Add search function
  • Loading branch information
cliffckerr committed Jul 16, 2020
2 parents 7fa0d41 + 32a238a commit 1892d9b
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 3 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file.

By import convention, components of the Sciris library are listed beginning with `sc.`, e.g. `sc.odict()`.

## Version 0.17.2 (2020-07-13)
1. `sc.search()` is a new function to find nested attributes/keys within objects or dictionaries.

## Version 0.17.1 (2020-07-07)
1. `sc.Blobject` has been modified to allow more flexibility with saving (e.g., `Path` objects).

Expand Down
48 changes: 47 additions & 1 deletion sciris/sc_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -1945,7 +1945,7 @@ def mprofile(run, follow=None, *args, **kwargs):
### NESTED DICTIONARY FUNCTIONS
##############################################################################

__all__ += ['getnested', 'setnested', 'makenested', 'iternested', 'mergenested', 'flattendict']
__all__ += ['getnested', 'setnested', 'makenested', 'iternested', 'mergenested', 'flattendict', 'search']

docstring = '''
Four little functions to get and set data from nested dictionaries. The first two were adapted from:
Expand Down Expand Up @@ -2091,6 +2091,52 @@ def flattendict(input_dict: dict, sep: str = None, _prefix=None) -> dict:

return output_dict

def search(obj, attribute, _trace=''):
"""
Find a key or attribute within a dictionary or object.
This function facilitates finding nested key(s) or attributes within an object,
by searching recursively through keys or attributes.
Args:
obj: A dict or class with __dict__ attribute
attribute: The substring to search for
_trace: Not for user input - internal variable used for recursion
Returns:
A list of matching attributes. The items in the list are the Python
strings used to access the attribute (via attribute or dict indexing)
Example::
nested = {'a':{'foo':1, 'bar':2}, 'b':{'bar':3, 'cat':4}}
matches = sc.search(nested, 'bar') # Returns ['["a"]["bar"]', '["b"]["bar"]']
"""

matches = []

if isinstance(obj, dict):
d = obj
elif hasattr(obj, '__dict__'):
d = obj.__dict__
else:
return matches

for attr in d:

if isinstance(obj, dict):
s = _trace + f'["{attr}"]'
else:
s = _trace + f'.{attr}'

if attribute in attr:
matches.append(s)

matches += search(d[attr], attribute, s)

return matches

##############################################################################
### CLASSES
Expand Down
4 changes: 2 additions & 2 deletions sciris/sc_version.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
__all__ = ['__version__', '__versiondate__', '__license__']

__version__ = '0.17.1'
__versiondate__ = '2020-07-07'
__version__ = '0.17.2'
__versiondate__ = '2020-07-13'
__license__ = 'Sciris %s (%s) -- (c) Sciris.org' % (__version__, __versiondate__)
9 changes: 9 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ def test_nested_dicts():
return dict3


def test_search():
sc.heading('Testing search')
nested = {'a':{'foo':1, 'bar':2}, 'b':{'bar':3, 'cat':4}}
matches = sc.search(nested, 'bar') # Returns ['["a"]["bar"]', '["b"]["bar"]']
print(matches)
return matches


def test_progress_bar():
sc.heading('Progress bar')
n = 50
Expand All @@ -305,6 +313,7 @@ def test_progress_bar():
flat = test_flattendict()
md = test_mergedicts()
nested = test_nested_dicts()
matches = test_search()
ind = test_progress_bar()

sc.toc()
Expand Down

0 comments on commit 1892d9b

Please sign in to comment.