我正在尝试创建一个替换嵌套字典键中的点的泛型函数。我有一个非泛型函数,深入3级,但必须有一种方法来做这个泛型。任何帮助表示赞赏!到目前为止我的代码:
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)
更新:回答我自己的问题,我使用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
答案 0 :(得分:26)
是的,有更好的方法:
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
(编辑:这是递归,更多关于Wikipedia。)
答案 1 :(得分:7)
我使用了@horejsek的代码,但我修改了它以接受带有列表的嵌套字典和替换字符串的函数。
我有一个类似的问题需要解决:我想用下划线小写约定替换camel case约定的键,反之亦然。
def change_dict_naming_convention(d, convert_function):
"""
Convert a nested dictionary from one convention to another.
Args:
d (dict): dictionary (nested or not) to be converted.
convert_function (func): function that takes the string in one convention and returns it in the other one.
Returns:
Dictionary with the new keys.
"""
new = {}
for k, v in d.iteritems():
new_v = v
if isinstance(v, dict):
new_v = change_dict_naming_convention(v, convert_function)
elif isinstance(v, list):
new_v = list()
for x in v:
new_v.append(change_dict_naming_convention(x, convert_function))
new[convert_function(k)] = new_v
return new
答案 2 :(得分:5)
实际上,所有答案都包含错误,可能导致错误输入结果。
我会接受@ngenain的回答并稍微改进一下。
我的解决方案会关注来自dict
(OrderedDict
,defaultdict
等)的类型,以及list
,set
和tuple
类型。
我还在函数的开头做了一个简单的类型检查,以减少比较次数(可能会在大量数据中提供一点速度)。
适用于Python 3.将obj.items()
替换为obj.iteritems()
以替换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
如果我理解正确的需求,大多数用户都希望将密钥转换为使用不允许密钥名称中的点的mongoDB。
答案 3 :(得分:4)
这是一个简单的递归解决方案,可以处理嵌套列表和字典。
def change_keys(obj, convert):
"""
Recursivly goes through the dictionnary obj and replaces keys with the convert function.
"""
if isinstance(obj, dict):
new = {}
for k, v in obj.iteritems():
new[convert(k)] = change_keys(v, convert)
elif isinstance(obj, list):
new = []
for v in obj:
new.append(change_keys(v, convert))
else:
return obj
return new
答案 4 :(得分:2)
你必须删除原始密钥,但是你不能在循环体中执行它,因为它会抛出RunTimeError:字典在迭代期间改变了大小。
要解决此问题,请迭代原始对象的副本,但修改原始对象:
def change_keys(obj):
new_obj = obj
for k in new_obj:
if hasattr(obj[k], '__getitem__'):
change_keys(obj[k])
if '.' in k:
obj[k.replace('.', '$')] = obj[k]
del obj[k]
>>> foo = {'foo': {'bar': {'baz.121': 1}}}
>>> change_keys(foo)
>>> foo
{'foo': {'bar': {'baz$121': 1}}}
答案 5 :(得分:0)
这是@horejsek的答案的1-liner变体,它对喜欢的人使用dict理解:
def print_dict(d):
return {k.replace('.', '-'): print_dict(v) for k, v in d.items()} if isinstance(d, dict) else d
我仅在Python 2.7中对此进行了测试
答案 6 :(得分:0)
您可以将所有内容转储到JSON 替换整个字符串并重新加载JSON
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
或者使用单线纸
def short_replace(data, old, new):
return json.loads(json.dumps(data).replace(old, new))
答案 7 :(得分:0)
我猜您遇到了和我一样的问题,将字典插入到 MongoDB 集合中,在尝试插入包含带点 (.) 键的字典时遇到异常。
此解决方案与此处的大多数其他答案基本相同,但它稍微紧凑一些,并且可读性可能较差,因为它使用单个语句并递归调用自身。对于 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() }