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
Yes, there exists better way:
def print_dict(d): new = {} for k, v in d.iteritems(): if isinstance(v, dict): v = print_dict(v) new[k.replace('.', '-')] = v return new
(Edit: It's recursion, more on Wikipedia.)
Actually all of the answers contain a mistake that may lead to wrong typing in the result.
I'd take the answer of @ngenain and improve it a bit below.
My solution will take care about the types derived from dict
(OrderedDict
, defaultdict
, etc) and also about not only list
, but set
and tuple
types.
I also do a simple type check in the beginning of the function for the most common types to reduce the comparisons count (may give a bit of speed in the large amounts of the data).
Works for Python 3. Replace obj.items()
with obj.iteritems()
for Py2.
def change_keys(obj, convert): """ Recursively goes through the dictionary obj and replaces keys with the convert function. """ if isinstance(obj, (str, int, float)): return obj if isinstance(obj, dict): new = obj.__class__() for k, v in obj.items(): new[convert(k)] = change_keys(v, convert) elif isinstance(obj, (list, set, tuple)): new = obj.__class__(change_keys(v, convert) for v in obj) else: return obj return new
If I understand the needs right, most of users want to convert the keys to use them with mongoDB that does not allow dots in key names.
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