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.
source to share
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
.
source to share
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.
source to share
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
source to share
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
source to share