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

Filter a dict of dict

How can I check if the characters in a string are in a dictionary of values?

Asked By: StatsSorceress

||

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
Answered By: adrtam

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
Answered By: Reedinationer

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
Answered By: ggorlen

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
Answered By: Jab

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
Answered By: blhsing
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.