What we want to do
Replace some_key
's value of '12345'
in this dictionary:
data = {
'top-level': {
'second-level': {
'some_key': '12345',
'some_other_key': '54321',
'another-level': {
'nested_key': 'abc'
}
},
'another-second-level': {
'some_key': 'def'
}
}
}
with some_new_key
's value of '67890'
from this dictionary:
new_data = {
'level-1': {
'level-2': {
'level-3': {
'some_new_key': '67890',
'some_other_new_key': '09876'
}
}
}
}
Getting a nested value
We'll repeatedly attempt to get the next nested part of the dictionary. The final item in the path should get us the value of that item. If at any point it can't find the next item, this function will just return an empty string.
import copy
def get_nested_value(dictionary, nested_path):
"""
Return a value from a dictionary given a path-like nesting of keys.
Will default to an empty string if value cannot be found.
Args:
dictionary (dict): a dictionary
nested_path (str): nested/path/to/key
Returns:
?: Value from dict
"""
replacement_value_path = nested_path.split('/')
replacement_value = copy.deepcopy(dictionary)
for item in replacement_value_path:
replacement_value = replacement_value.get(item, {})
if replacement_value == {}:
replacement_value = ''
return replacement_value
Replacing a nested value
This next function is a fun one that works because of a few things:
- Dictionaries in Python are mutable (meaning we can change them after creation)
- Most things in Python are objects (including dictionaries)
- Passing objects into functions in Python allow you to change the original object (if it's mutable, e.g. dictionaries)
Those three things allow the following to actually update the original dictionary:
def replace(data, path_to_key, replacement_value):
"""
Will replace a nested value in a dict with the given value.
Args:
data (dict): a dictionary
path_to_key (str): nested/path/to/key. The value of this key will be
replaced
replacement_value (str): Replacement value for the key from
path_to_key
"""
nested_path_to_replace = path_to_key.split('/')
# follow dict to final key
for item in nested_path_to_replace[:-1]:
data = data.get(item, {})
data.update(
{str(nested_path_to_replace[-1]): replacement_value}
)
Putting it all together!
data = {
'top-level': {
'second-level': {
'some_key': '12345',
'some_other_key': '54321',
'another-level': {
'nested_key': 'abc'
}
},
'another-second-level': {
'some_key': 'def'
}
}
}
new_data = {
'level-1': {
'level-2': {
'level-3': {
'some_new_key': '67890',
'some_other_new_key': '09876'
}
}
}
}
replacement_key = get_nested_value(
new_data, 'level-1/level-2/level-3/some_new_key')
replace(
data, 'top-level/second-level/some_key', replacement_key)
print(data)
After that, the data
dictionary will have '67890'
as the value for some_key
. Note that we also did not modify any of the other items either, which is good. We just replaced what we wanted to.
{
'top-level': {
'second-level': {
'some_key': '67890',
'some_other_key': '54321',
'another-level': {
'nested_key': 'abc'
}
},
'another-second-level': {
'some_key': 'def'
}
}
}