Permalink
Browse files

info_extractor/Utilities: Add search functions

Adds search methods to recursively search for keys and
`dict` like values inside a nested object.

Related to #42
  • Loading branch information...
satwikkansal committed Jul 2, 2017
1 parent 6cdfb37 commit ddf0779e3645baaab6ab199acb5f2a1320590dc5
Showing with 208 additions and 0 deletions.
  1. +67 −0 coala_quickstart/info_extractors/Utilities.py
  2. +141 −0 tests/info_extractors/UtilitiesTest.py
@@ -0,0 +1,67 @@
def search_object_recursively(search_object,
key,
value=None,
prepath=(),
idx=-1):
"""
Searches for the given ``key`` and ``value`` in an object
containing nested lists, tuples and dicts.
:param search_object:
object to be searched
:param key:
key to look for, can be a dictionary as well.
:param value:
optional, if provided match the value of `key` parameter
with this `value` parameter and the result will consist of
all the objects having this key-value pair
:param prepath:
path traversed recursively so far.
:param idx:
index of current element being traversed while traversing a
list or tuple of elements.
:return:
A list of dicts of the form
{
"object": list or dict that match the search criteria,
"path": tuple of path to the object from the root of the
`search_object`
}
"""
results = []
supported_types = (list, tuple, dict)
if isinstance(search_object, (list, tuple)):
for i in search_object:
idx += 1
results += search_object_recursively(
i, key, value=value, prepath=prepath, idx=idx)
elif isinstance(search_object, dict):
for k, v in search_object.items():
path = prepath + (k,) if idx < 0 else prepath + (idx, k,)
if k == key:
if value is not None and v == value:
results.append({
"object": search_object,
"path": path
})
elif value is None:
results.append({
"object": v,
"path": path
})
elif isinstance(v, supported_types):
results += search_object_recursively(v,
key,
value=value,
prepath=path,
idx=-1)
else:
raise TypeError(
"The object to be searched should only contain these types: {}"
.format(','.join([str(t) for t in supported_types])))
return results
@@ -0,0 +1,141 @@
import unittest
from coala_quickstart.info_extractors import Utilities
class UtititiesTest(unittest.TestCase):
def setUp(self):
self.simple_dict = {
"key1": "value1",
"key2": "value2"
}
self.nested_dict = {
"key1": {
"key1.1": "value1.1"
}
}
self.nested_dict_with_list = {
"key1": {
"key1.1": [
{
"key_a": "value_a"
},
{
"key_b": [
{
"key_b_1": "value_b_1"
}]
}]
}
}
self.nested_list_with_dict = [
{
"key1": "value1",
"key2": "value2"
},
{
"key3": "value3",
"key4": "value4"
}
]
self.dict_with_repeated_structure = {
"key1": {
"key1.1": "value1.1"
},
"key2": {
"key1.1": "value1.1",
"key2.1": "value2.1"
}
}
def test_search_object_recursively(self):
uut = Utilities.search_object_recursively
def assert_value_and_path(search_obj,
search_key,
search_value,
expected_results,
expected_paths):
search_results = uut(search_obj, search_key, search_value)
for obj, path in zip(expected_results, expected_paths):
expected_result = {
"object": obj,
"path": path
}
self.assertIn(expected_result, search_results)
assert_value_and_path(
self.simple_dict, "key1", None, ["value1"], [("key1",)])
assert_value_and_path(self.simple_dict,
"key1",
"value1",
[{
"key1": "value1",
"key2": "value2"
}],
[("key1",)])
assert_value_and_path(self.nested_dict,
"key1.1",
None,
["value1.1"],
[("key1", "key1.1")])
assert_value_and_path(self.nested_dict,
"key1.1",
"value1.1",
[{
"key1.1": "value1.1"
}],
[("key1", "key1.1")])
assert_value_and_path(self.nested_dict_with_list,
"key_b_1",
None,
["value_b_1"],
[("key1", "key1.1", 1, "key_b", 0, "key_b_1")])
assert_value_and_path(self.nested_dict_with_list,
"key_b_1",
"value_b_1",
[{
"key_b_1": "value_b_1"
}],
[("key1", "key1.1", 1, "key_b", 0, "key_b_1")])
assert_value_and_path(self.nested_list_with_dict,
"key3",
None,
["value3"],
[(1, "key3")])
assert_value_and_path(self.nested_list_with_dict,
"key3",
"value3",
[{
"key3": "value3",
"key4": "value4"
}],
[(1, "key3")])
assert_value_and_path(self.dict_with_repeated_structure,
"key1.1",
None,
["value1.1", "value1.1"],
[("key1", "key1.1"), ("key2", "key1.1")])
assert_value_and_path(self.dict_with_repeated_structure,
"key1.1",
"value1.1",
[
{
"key1.1": "value1.1",
"key2.1": "value2.1"
},
{
"key1.1": "value1.1",
}
],
[("key2", "key1.1"), ("key1", "key1.1")])

0 comments on commit ddf0779

Please sign in to comment.