How to recursively replace character in keys of a nested dictionary?

Viewed 26366

I'm trying to create a generic function that replaces dots in keys of a nested dictionary. I have a non-generic function that goes 3 levels deep, but there must be a way to do this generic. Any help is appreciated! My code so far:

output = {'key1': {'key2': 'value2', 'key3': {'key4 with a .': 'value4', 'key5 with a .': 'value5'}}} 

def print_dict(d):
    new = {}
    for key,value in d.items():
        new[key.replace(".", "-")] = {}
        if isinstance(value, dict):
            for key2, value2 in value.items():
                new[key][key2] = {}
                if isinstance(value2, dict):
                    for key3, value3 in value2.items():
                        new[key][key2][key3.replace(".", "-")] = value3
                else:
                    new[key][key2.replace(".", "-")] = value2
        else:
            new[key] = value
    return new

print print_dict(output)

UPDATE: to answer my own question, I made a solution using json object_hooks:

import json

def remove_dots(obj):
    for key in obj.keys():
        new_key = key.replace(".","-")
        if new_key != key:
            obj[new_key] = obj[key]
            del obj[key]
    return obj

output = {'key1': {'key2': 'value2', 'key3': {'key4 with a .': 'value4', 'key5 with a .': 'value5'}}}
new_json = json.loads(json.dumps(output), object_hook=remove_dots) 

print new_json
9 Answers

You can dump everything to a JSON replace through the whole string and load the JSON back

def nested_replace(data, old, new):
    json_string = json.dumps(data)
    replaced = json_string.replace(old, new)
    fixed_json = json.loads(replaced)
    return fixed_json

Or use a one-liner

def short_replace(data, old, new):
    return json.loads(json.dumps(data).replace(old, new))

Here's a 1-liner variant of @horejsek 's answer using dict comprehension for those who prefer:

def print_dict(d):
    return {k.replace('.', '-'): print_dict(v) for k, v in d.items()} if isinstance(d, dict) else d

I've only tested this in Python 2.7

I am guessing you have the same issue as I have, inserting dictionaries into a MongoDB collection, encountering exceptions when trying to insert dictionaries that have keys with dots (.) in them.

This solution is essentially the same as most other answers here, but it is slightly more compact, and perhaps less readable in that it uses a single statement and calls itself recursively. For Python 3.

def replace_keys(my_dict):
    return { k.replace('.', '(dot)'): replace_keys(v) if type(v) == dict else v for k, v in my_dict.items() }
Related