Recursively replace dictionary values ​​with the corresponding key

I am trying to take a dictionary and find all the keys that match key

and replace them with a value replace_value

. Dictionaries in theory can be infinitely deep, so they need to be done recursively.

My current solution replaces the values ​​correctly, but throws an exception saying "the maximum recursion depth was exceeded when calling a Python object" (not to mention that it doesn't use recursion well with return values).

def replace_item(obj, key, replace_value):
    """
    Replaces the dictionary value of key with replace_value in the obj dictionary.
    """
    if key in obj:
        obj[key] = replace_value

    for k, v in obj.items():
        if isinstance(v, dict):
            item = replace_item(v, key, replace_value)

            if item is not None:
                item = replace_value

     return obj

      

An example of the operation performed would be as follows:

Original dictionary

person_dict = {
    "name": "Alex",
    "sex": "M",
    "title": "Engineer",
    "misc": {
        "mailbox": "3A",
        "work_type": "remote"
    }
}

      

I would then make a call replace_item(person_dict, "work_type", "office")

that I would rather change to return the updated dictionary ( person_dict = replace_item(person_dict, "work_type", "office")

).

Dictionary of substituted values

person_dict = {
    "name": "Alex",
    "sex": "M",
    "title": "Engineer"
    "misc": {
        "mailbox": "3A",
        "work_type": "office"
    }
}

      

How can I fix my recursion? Thank you in advance.

+3


source to share


4 answers


You have strange behavior when you expect it return

, but you don't. Also your description implies that it should replace nested keys, but you will skip your code if the dictionary at the top level has no key, but at a lower level. I believe the code below accomplishes what you described:

def replace_item(obj, key, replace_value):
    for k, v in obj.items():
        if isinstance(v, dict):
            obj[k] = replace_item(v, key, replace_value)
    if key in obj:
        obj[key] = replace_value
    return obj

      



EDIT . As @dashiell suggested, moving the top-level reassignment after recursive find / replace eliminates the infinite recursion trap with having key

in replace_value

.

+2


source


Functional style is used here:

def replace(obj, key, val):
    return {k: replace(val if k == key else v, key, val) 
        for k,v in obj.items()} if isinstance(obj, dict) else obj

      



This is inefficient in Python (since all meanings / subtitles are recreated), but it demonstrates how to solve your problem without side effects and without mutating objects.

+2


source


def replace_item(obj, key, replace_value):
    """
    Replaces the dictionary value of key with replace_value in the obj dictionary.
    """
    if key in obj:
        obj[key] = replace_value

    for k, v in obj.items():
        if isinstance(v, dict):
            replace_item(v, key, replace_value)

      

I think this is enough .. no need for additional variables and on most systems the default recursion depth is around 1000, you can change it https://docs.python.org/3/library/sys.html#sys.setrecursionlimit

+1


source


If you want to replace "full nested dictionnay with arrays" you can use this snippet:

It will replace any "old_value" with "new_value". This is roughly what a deep dictionary recovery does. It can even work with List or Str / int given as a first level input parameter.

def update_values_dict(original_dict, future_dict, old_value, new_value):
    # Recursively updates values of a nested dict by performing recursive calls

    if isinstance(original_dict, Dict):
        # It a dict
        tmp_dict = {}
        for key, value in original_dict.items():
            tmp_dict[key] = update_values_dict(value, future_dict, old_value, new_value)
        return tmp_dict
    elif isinstance(original_dict, List):
        # It a List
        tmp_list = []
        for i in original_dict:
            tmp_list.append(update_values_dict(i, future_dict, old_value, new_value))
        return tmp_list
    else:
        # It not a dict, maybe a int, a string, etc.
    return original_dict if original_dict != old_value else new_value

      

0


source







All Articles