From 77a3b94f5ba6c75dc2d14883791c17e348062c73 Mon Sep 17 00:00:00 2001 From: Fabio Caccamo Date: Mon, 13 Jan 2020 14:53:16 +0100 Subject: [PATCH] Added search method. --- benedict/dicts/__init__.py | 6 ++++++ benedict/utils/dict_util.py | 20 +++++++++++++++++ tests/test_benedict.py | 43 +++++++++++++++++++++++++++++++++++++ 3 files changed, 69 insertions(+) diff --git a/benedict/dicts/__init__.py b/benedict/dicts/__init__.py index 3b9b3f2..6373619 100644 --- a/benedict/dicts/__init__.py +++ b/benedict/dicts/__init__.py @@ -125,6 +125,12 @@ class benedict(IODict, KeypathDict, ParseDict): """ dict_util.rename(self, key, key_new) + def search(self, query, in_keys=True, in_values=True, exact=False, case_sensitive=False): + """ + Search and return a list of items (dict, key, value, ) matching the given query. + """ + return dict_util.search(self, query, in_keys, in_values, exact, case_sensitive) + def standardize(self): """ Standardize all dict keys (e.g. 'Location Latitude' -> 'location_latitude'). diff --git a/benedict/utils/dict_util.py b/benedict/utils/dict_util.py index 60f2bc6..58cfa7e 100644 --- a/benedict/utils/dict_util.py +++ b/benedict/utils/dict_util.py @@ -155,6 +155,26 @@ def resolve(d, keys, **kwargs): return result +def search(d, query, in_keys=True, in_values=True, exact=False, case_sensitive=True): + items = [] + def s(value): + # TODO: add regex support + q_is_str = isinstance(query, string_types) + q = query.lower() if q_is_str and not case_sensitive else query + v_is_str = isinstance(value, string_types) + v = value.lower() if v_is_str and not case_sensitive else value + if exact: + return q == v + elif q_is_str and v_is_str: + return q in v + return False + def f(item_dict, item_key, item_value): + if (in_keys and s(item_key)) or (in_values and s(item_value)): + items.append((item_dict, item_key, item_value, )) + traverse(d, f) + return items + + def standardize(d): def f(item, item_key, item_value): if isinstance(item_key, string_types): diff --git a/tests/test_benedict.py b/tests/test_benedict.py index ca10a5a..3322f52 100644 --- a/tests/test_benedict.py +++ b/tests/test_benedict.py @@ -1225,6 +1225,49 @@ b: with self.assertRaises(KeyError): b.rename('aa', 'b') + def test_search(self): + d = { + 'a': 'Hello world', + 'b': 'Hello world!', + 'c': { + 'd': True, + 'e': ' hello world ', + 'f': { + 'g': 'HELLO', + 'h': 12345, + 'hello': True, + }, + }, + 'Hello world': 'Hello World', + } + b = benedict(d) + + results = b.search('Hello', in_keys=False, in_values=False, exact=True, case_sensitive=True) + self.assertEqual(len(results), 0) + self.assertEqual(results, []) + + results = b.search('Hello', in_keys=False, in_values=True, exact=True, case_sensitive=True) + self.assertEqual(len(results), 0) + self.assertEqual(results, []) + + results = b.search('Hello', in_keys=False, in_values=True, exact=True, case_sensitive=False) + self.assertEqual(len(results), 1) + self.assertTrue((d['c']['f'], 'g', d['c']['f']['g'], ) in results) + + results = b.search('hello', in_keys=True, in_values=True, exact=False, case_sensitive=False) + self.assertEqual(len(results), 6) + self.assertTrue((d, 'a', d['a'], ) in results) + self.assertTrue((d, 'b', d['b'], ) in results) + self.assertTrue((d['c'], 'e', d['c']['e'], ) in results) + self.assertTrue((d['c']['f'], 'g', d['c']['f']['g'], ) in results) + self.assertTrue((d['c']['f'], 'hello', d['c']['f']['hello'], ) in results) + self.assertTrue((d, 'Hello world', d['Hello world'], ) in results) + + results = b.search('hello', in_keys=True, in_values=False, exact=False, case_sensitive=False) + self.assertEqual(len(results), 2) + self.assertTrue((d['c']['f'], 'hello', d['c']['f']['hello'], ) in results) + self.assertTrue((d, 'Hello world', d['Hello world'], ) in results) + # def test_setdefault(self): # d = { # 'a': 1,