I'm trying to take a dictionary and find all of the keys that match key
and replace their value with replace_value
. The dictionaries can in theory be infinitely deep, so it must be done recursively.
My current solution replaces values correctly, but raises an exception saying "maximum recursion depth exceeded while calling a Python object" (not to mention the fact that it's poor use of recursion with no 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 an operation it would perform would be the following:
Original Dictionary
person_dict = {
"name": "Alex",
"sex": "M",
"title": "Engineer",
"misc": {
"mailbox": "3A",
"work_type": "remote"
}
}
Then I'd make a call to replace_item(person_dict, "work_type", "office")
, which I'd preferably like to change over to returning the updated dictionary (person_dict = replace_item(person_dict, "work_type", "office")
).
Replaced Value Dictionary
person_dict = {
"name": "Alex",
"sex": "M",
"title": "Engineer"
"misc": {
"mailbox": "3A",
"work_type": "office"
}
}
How can I go about fixing my recursion?
You have some strange behavior where you are expecting a return
but you don't have one. Also your description implies it should replace nested keys, but your code you will miss when a dictionary at the top level does not have the key but at a lower level does. I believe the below code 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 the recursive search/replace avoids the infinite recursion trap of having key
exist in the replace_value
.
Here's a functional-style take:
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
It's not efficient in Python (since all values/subdicts are re-created), but demonstrates how to solve your problem without side-effects and without mutating objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With