How to find whether a string either a key or a value in a dict or in a dict of dicts
Question:
This looks like it should be simple, but for some reason I can’t get there.
I have dicts that could be in several possible formats:
dict1 = {"repo": "val1", "org":"val2"}
dict2 = {"key1":"repo", "key2":"org"}
dict3 = {"key1":"org and stuff"}
dict4 = {"org and stuff":"value1"}
dict5 = {"key1":{"value1":"value2"}, "key2":{"1":"org"}}
dict6 = {"org":{"value1":"value2"}, "key2":{"value1":"value2"}}
dict7 = {"org":{"subkey1":{"value1":"value2"}}, "key2":{"value1":"value2"}}
dict8 = {"key1":{"subkey1":{"value1":"org"}}, "key2":{"value1":"value2"}}
I want to search for the string 'org'
and if it is anywhere in the dict (key, value, key[subkey][value], etc.), return true. I do not want partial string matches.
That is, I’m looking for the following result:
True
True
False
False
True
True
True
True
I have read these questions, but none of them quite answer because I could have nested dicts:
How to search for all the characters from a string in all Dictionary.Values()
Generic Function to replace value of a key in a dict or nested dict or list of dicts
Find dictionary items whose key matches a substring
How can I check if the characters in a string are in a dictionary of values?
Answers:
Use recursion!
def indict(thedict, thestring):
if thestring in thedict:
return True
for val in thedict.values():
if isinstance(val, dict) and indict(val, thestring):
return True
elif isinstance(val, str) and val == thestring:
return True
return False
It looks like adrtam got here first, but I basically came up with the same thing
def searcher(input_dict, search_item):
for key, value in input_dict.items():
if search_item == key or search_item == value:
return True
elif type(value) is dict:
searcher(input_dict[key], search_item) #search recursively
return False
Recursion is a good choice. Iterate through the keys and values of the dictionary, looking for the target string and recursing on any nested dictionary values we find along the way:
def in_nested_dict(needle, haystack):
if isinstance(haystack, dict):
return any(
k == needle or v == needle or in_nested_dict(needle, v)
for k, v in haystack.items()
)
return False
if __name__ == "__main__":
tests = [
{"repo": "val1", "org": "val2"},
{"key1": "repo", "key2": "org"},
{"key1": "org and stuff"},
{"org and stuff": "value1"},
{"key1": {"value1": "value2"}, "key2": {"1": "org"}},
{"org": {"value1": "value2"}, "key2": {"value1": "value2"}},
{"org": {"subkey1": {"value1": "value2"}}, "key2": {"value1": "value2"}},
{"key1": {"subkey1": {"value1": "org"}}, "key2": {"value1": "value2"}},
]
for test in tests:
print(in_nested_dict("org", test))
Output:
True
True
False
False
True
True
True
True
Technically you could just convert the dict to a string and search if the word exists using re
. Plus this requires no loops and should be faster/simpler than using loops/recursion if your dicts happen to be very large. Also if you have sub lists or tuples and such
import re
def in_dict(data, word):
return bool(re.search(f"'{word}'", str(data)))
For proof of concept, using your dicts I tested and this returns your desired results.
dicts = dict1, dict2, dict3, dict4, dict5, dict6, dict7, dict8
for d in dicts:
print(in_dict(d, 'org'))
This prints:
True
True
False
False
True
True
True
True
True
You can use a recursive function that returns True
if the search string is in any of the keys or values of the given dict, or if the given dict is actually not a dict, returns if the search string is equal to it:
def is_in(d, s):
return s in d or any(is_in(v, s) for v in d.values()) if isinstance(d, dict) else d == s
so that:
for d in [
{"repo": "val1", "org":"val2"},
{"key1":"repo", "key2":"org"},
{"key1":"org and stuff"},
{"org and stuff":"value1"},
{"key1":{"value1":"value2"}, "key2":{"1":"org"}},
{"org":{"value1":"value2"}, "key2":{"value1":"value2"}},
{"org":{"subkey1":{"value1":"value2"}}, "key2":{"value1":"value2"}},
{"key1":{"subkey1":{"value1":"org"}}, "key2":{"value1":"value2"}}]:
print(is_in(d, 'org'))
outputs:
True
True
False
False
True
True
True
True
This looks like it should be simple, but for some reason I can’t get there.
I have dicts that could be in several possible formats:
dict1 = {"repo": "val1", "org":"val2"}
dict2 = {"key1":"repo", "key2":"org"}
dict3 = {"key1":"org and stuff"}
dict4 = {"org and stuff":"value1"}
dict5 = {"key1":{"value1":"value2"}, "key2":{"1":"org"}}
dict6 = {"org":{"value1":"value2"}, "key2":{"value1":"value2"}}
dict7 = {"org":{"subkey1":{"value1":"value2"}}, "key2":{"value1":"value2"}}
dict8 = {"key1":{"subkey1":{"value1":"org"}}, "key2":{"value1":"value2"}}
I want to search for the string 'org'
and if it is anywhere in the dict (key, value, key[subkey][value], etc.), return true. I do not want partial string matches.
That is, I’m looking for the following result:
True
True
False
False
True
True
True
True
I have read these questions, but none of them quite answer because I could have nested dicts:
How to search for all the characters from a string in all Dictionary.Values()
Generic Function to replace value of a key in a dict or nested dict or list of dicts
Find dictionary items whose key matches a substring
How can I check if the characters in a string are in a dictionary of values?
Use recursion!
def indict(thedict, thestring):
if thestring in thedict:
return True
for val in thedict.values():
if isinstance(val, dict) and indict(val, thestring):
return True
elif isinstance(val, str) and val == thestring:
return True
return False
It looks like adrtam got here first, but I basically came up with the same thing
def searcher(input_dict, search_item):
for key, value in input_dict.items():
if search_item == key or search_item == value:
return True
elif type(value) is dict:
searcher(input_dict[key], search_item) #search recursively
return False
Recursion is a good choice. Iterate through the keys and values of the dictionary, looking for the target string and recursing on any nested dictionary values we find along the way:
def in_nested_dict(needle, haystack):
if isinstance(haystack, dict):
return any(
k == needle or v == needle or in_nested_dict(needle, v)
for k, v in haystack.items()
)
return False
if __name__ == "__main__":
tests = [
{"repo": "val1", "org": "val2"},
{"key1": "repo", "key2": "org"},
{"key1": "org and stuff"},
{"org and stuff": "value1"},
{"key1": {"value1": "value2"}, "key2": {"1": "org"}},
{"org": {"value1": "value2"}, "key2": {"value1": "value2"}},
{"org": {"subkey1": {"value1": "value2"}}, "key2": {"value1": "value2"}},
{"key1": {"subkey1": {"value1": "org"}}, "key2": {"value1": "value2"}},
]
for test in tests:
print(in_nested_dict("org", test))
Output:
True
True
False
False
True
True
True
True
Technically you could just convert the dict to a string and search if the word exists using re
. Plus this requires no loops and should be faster/simpler than using loops/recursion if your dicts happen to be very large. Also if you have sub lists or tuples and such
import re
def in_dict(data, word):
return bool(re.search(f"'{word}'", str(data)))
For proof of concept, using your dicts I tested and this returns your desired results.
dicts = dict1, dict2, dict3, dict4, dict5, dict6, dict7, dict8
for d in dicts:
print(in_dict(d, 'org'))
This prints:
True
True
False
False
True
True
True
True
True
You can use a recursive function that returns True
if the search string is in any of the keys or values of the given dict, or if the given dict is actually not a dict, returns if the search string is equal to it:
def is_in(d, s):
return s in d or any(is_in(v, s) for v in d.values()) if isinstance(d, dict) else d == s
so that:
for d in [
{"repo": "val1", "org":"val2"},
{"key1":"repo", "key2":"org"},
{"key1":"org and stuff"},
{"org and stuff":"value1"},
{"key1":{"value1":"value2"}, "key2":{"1":"org"}},
{"org":{"value1":"value2"}, "key2":{"value1":"value2"}},
{"org":{"subkey1":{"value1":"value2"}}, "key2":{"value1":"value2"}},
{"key1":{"subkey1":{"value1":"org"}}, "key2":{"value1":"value2"}}]:
print(is_in(d, 'org'))
outputs:
True
True
False
False
True
True
True
True