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:
parent
7bfbf5ada4
commit
756ad31763
3 changed files with 79 additions and 53 deletions
63
README.rst
63
README.rst
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -38,17 +47,31 @@ 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']))
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue