added tests for the new with_keys option.

modified:   README.rst
	modified:   nested_lookup/nested_lookup.py
	modified:   test_nested_loopkup.py
This commit is contained in:
Russell Ballestrini 2018-04-24 12:50:00 -07:00
parent 7bfbf5ada4
commit 756ad31763
3 changed files with 79 additions and 53 deletions

View file

@ -30,8 +30,8 @@ or install from source using::
cd nested-lookup cd nested-lookup
pip install . pip install .
tutorial quick tutorial
======== ==============
.. code-block:: python .. code-block:: python
@ -42,14 +42,20 @@ tutorial
>>> print(nested_lookup('taco', document)) >>> print(nested_lookup('taco', document))
[42, 69] [42, 69]
longer tutorial
===============
wild You may control the libraries behavior by passing some optional arguments.
========
We also have a `wild` mode that treats the given `key` as a case insensitive wild (defaults to `False`):
substring of all the keys in the document and returns any values which match. if `wild` is `True`, treat the given `key` as a case insensitive
substring when performing lookups.
For example: return_keys (defaults to `False`):
if `with_keys` is `True`, return a dictionary of all matched keys
and a list of values.
For example, given the following document:
.. code-block:: python .. code-block:: python
@ -61,9 +67,14 @@ For example:
'other' : { 'other' : {
'secondary_email' : 'test2@example.com', 'secondary_email' : 'test2@example.com',
'EMAIL_RECOVERY' : 'test3@example.com', 'EMAIL_RECOVERY' : 'test3@example.com',
'email_address' : 'test4@example.com',
}, },
}, },
We could act `wild` and find all the email addresses like this:
.. code-block:: python
results = nested_lookup( results = nested_lookup(
key = 'mail', key = 'mail',
document = my_document, document = my_document,
@ -71,43 +82,31 @@ For example:
) )
print(results) print(results)
['test1@example.com', 'test2@example.com', 'test3@example.com']
output
========
There are two `output` modes:
* `list`: the function returns a list of values corresponding to the matched keys.
* `dict`: the function returns a `dict` with the matched keys as keys and their corresponding values as values.
For example:
.. code-block:: python .. code-block:: python
from nested_lookup import nested_lookup ['test1@example.com', 'test4@example.com', 'test2@example.com', 'test3@example.com']
my_document = { Additionally, if you also needed the matched key names, you could do this:
'name' : 'Russell Ballestrini',
'email_address' : 'test1@example.com', .. code-block:: python
'other' : {
'secondary_email' : 'test2@example.com',
'EMAIL_RECOVERY' : 'test3@example.com',
},
},
results = nested_lookup( results = nested_lookup(
key = 'mail', key = 'mail',
document = my_document, document = my_document,
wild = True, wild = True,
output = 'dict' with_keys = True,
) )
print(results) print(results)
{'email_address': 'test1@example.com',
'secondary_email': 'test2@example.com', .. code-block:: python
'EMAIL_RECOVERY': 'test3@example.com'}
{
'email_address': ['test1@example.com', 'test4@example.com'],
'secondary_email': ['test2@example.com'],
'EMAIL_RECOVERY': ['test3@example.com']
}
misc misc

View file

@ -1,30 +1,34 @@
from six import iteritems from six import iteritems
def nested_lookup(key, document, wild=False, output='list'): from collections import defaultdict
"""Lookup a key in a nested document, return a list of values"""
if output == 'dict':
return dict(_nested_lookup(key, document, wild=wild, output=output))
else:
return list(_nested_lookup(key, document, wild=wild, output=output))
def _nested_lookup(key, document, wild=False, output='list'): def nested_lookup(key, document, wild=False, with_keys=False):
"""Lookup a key in a nested document, return a list of values"""
if with_keys:
d = defaultdict(list)
for k, v in _nested_lookup(key, document, wild=wild, with_keys=with_keys):
d[k].append(v)
return d
return list(_nested_lookup(key, document, wild=wild, with_keys=with_keys))
def _nested_lookup(key, document, wild=False, with_keys=False):
"""Lookup a key in a nested document, yield a value""" """Lookup a key in a nested document, yield a value"""
if isinstance(document, list): if isinstance(document, list):
for d in document: for d in document:
for result in _nested_lookup(key, d, wild=wild): for result in _nested_lookup(key, d, wild=wild, with_keys=with_keys):
yield result yield result
if isinstance(document, dict): if isinstance(document, dict):
for k, v in iteritems(document): for k, v in iteritems(document):
if key == k or (wild and key.lower() in k.lower()): if key == k or (wild and key.lower() in k.lower()):
if output == 'dict': if with_keys:
yield k, v yield k, v
else: else:
yield v yield v
elif isinstance(v, dict): elif isinstance(v, dict):
for result in _nested_lookup(key, v, wild=wild, output=output): for result in _nested_lookup(key, v, wild=wild, with_keys=with_keys):
yield result yield result
elif isinstance(v, list): elif isinstance(v, list):
for d in v: for d in v:
for result in _nested_lookup(key, d, wild=wild, output=output): for result in _nested_lookup(key, d, wild=wild, with_keys=with_keys):
yield result yield result

View file

@ -6,6 +6,15 @@ class TestNestedLookup(TestCase):
def setUp(self): def setUp(self):
self.subject_dict = {'a':1,'b':{'d':100},'c':{'d':200}} self.subject_dict = {'a':1,'b':{'d':100},'c':{'d':200}}
self.subject_dict2 = {
'name' : 'Russell Ballestrini',
'email_address' : 'test1@example.com',
'other' : {
'secondary_email' : 'test2@example.com',
'EMAIL_RECOVERY' : 'test3@example.com',
'email_address' : 'test4@example.com',
},
}
def test_nested_lookup(self): def test_nested_lookup(self):
results = nested_lookup('d', self.subject_dict) results = nested_lookup('d', self.subject_dict)
@ -37,18 +46,32 @@ class TestNestedLookup(TestCase):
def test_wild_nested_lookup(self): def test_wild_nested_lookup(self):
results = nested_lookup( results = nested_lookup(
key = 'mail', key = 'mail',
document = { document = self.subject_dict2
'name' : 'Russell Ballestrini',
'email_address' : 'test1@example.com',
'other' : {
'secondary_email' : 'test2@example.com',
'EMAIL_RECOVERY' : 'test3@example.com',
},
},
wild = True, wild = True,
) )
self.assertEqual(3, len(results)) self.assertEqual(4, len(results))
self.assertIn('test1@example.com', results) self.assertIn('test1@example.com', results)
self.assertIn('test2@example.com', results) self.assertIn('test2@example.com', results)
self.assertIn('test3@example.com', results) self.assertIn('test3@example.com', results)
def test_wild_with_keys_nested_lookup(self):
matches = nested_lookup(
key = 'mail',
document = self.subject_dict2,
wild = True,
with_keys = True,
)
self.assertEqual(3, len(matches))
self.assertIn('email_address', matches)
self.assertIn('secondary_email', matches)
self.assertIn('EMAIL_RECOVERY', matches)
self.assertSetEqual({'test1@example.com','test4@example.com'}, set(matches['email_address']))
self.assertIn('test2@example.com', matches['secondary_email'])
def test_nested_lookup_with_keys(self):
matches = nested_lookup('d', self.subject_dict, with_keys=True)
self.assertIn('d', matches)
self.assertEqual(2, len(matches['d']))
self.assertSetEqual({100,200}, set(matches['d']))