简体   繁体   中英

How to rename a deeply nested key in list of dictionaries (Python 3)?

Given the following dict (part of very long list of dicts):

{'diet': {'Diet 0': {'gender': 0,
   'nutrients': {'Alcohol': {'min': 0, 'max': 14, 'unit': 'oz'},
    'Caffeine': {'min': 0, 'max': 400, 'unit': 'mg'},
    'Copper': {'min': 0, 'max': 0, 'unit': 'mg'},
    'Calcium': {'min': 1000, 'max': 2500, 'unit': 'mg'},
    'Choline': {'min': 425, 'max': 3500, 'unit': 'mg'},
    'Cholesterol': {'min': 0, 'max': 300, 'unit': 'mg'},
    'Fluoride': {'min': 0, 'max': 0, 'unit': 'mg'},
    'SaturatedFat': {'min': 0, 'max': -1, 'unit': 'g'},
    'VitaminA': {'min': 2330, 'max': 10000, 'unit': 'IU'},
    'VitaminC': {'min': 75, 'max': 2000, 'unit': 'mg'},
    'VitaminD': {'min': 15, 'max': 100, 'unit': 'mcg'},
    'VitaminE': {'min': 15, 'max': 1000, 'unit': 'mg'},
    'VitaminK': {'min': 0, 'max': 0, 'unit': 'mcg'},
    'VitaminB1': {'min': 1.1, 'max': 0, 'unit': 'mg'},
    'VitaminB2': {'min': 0, 'max': 0, 'unit': 'mg'},
    'VitaminB5': {'min': 0, 'max': 0, 'unit': 'mg'},
    'VitaminB3': {'min': 0, 'max': 0, 'unit': 'mg'},
    'VitaminB6': {'min': 0, 'max': 0, 'unit': 'mg'},
    'VitaminB12': {'min': 2.4, 'max': 999999999, 'unit': 'mcg'},
    'Fiber': {'min': 21, 'max': 70, 'unit': 'g'},
    'Folate': {'min': 400, 'max': 1000, 'unit': 'mcg'},
    'FolicAcid': {'min': 0, 'max': 0, 'unit': 'mcg'},
    'Iodine': {'min': 0, 'max': 0, 'unit': 'mcg'},
    'Iron': {'min': 18.0, 'max': 45, 'unit': 'mg'},
    'Magnesium': {'min': 310, 'max': 350, 'unit': 'mg'},
    'Manganese': {'min': nan, 'max': 0, 'unit': 'mg'},
    'Phosphorus': {'min': 700, 'max': 4000, 'unit': 'mg'},
    'Potassium': {'min': 2600, 'max': 0, 'unit': 'mg'},
    'Selenium': {'min': 0, 'max': 0, 'unit': 'mcg'},
    'Sodium': {'min': 500, 'max': 2300, 'unit': 'mg'},
    'Sugar': {'min': nan, 'max': 24, 'unit': 'g'},
    'Zinc': {'min': 8.0, 'max': 40, 'unit': 'mg'}}}}}

I have 10000 of these dicts in a list, I want to change the SaturatedFat key to be Saturated Fat . Tried with ast :

import ast
ast.literal_eval(str(diet_specs_dicts[0]).replace("'SaturatedFat':","'Saturated Fat':"))

Returns an error:

--------------------------------------------------------------------------- ValueError Traceback (most recent call last) in 1 import ast ----> 2 ast.literal_eval(str(diet_specs_dicts[0]).replace("'SaturatedFat':","'Saturated Fat':"))

/usr/lib/python3.8/ast.py in literal_eval(node_or_string) 97 return left - right 98 return _convert_signed_num(node) ---> 99 return _convert(node_or_string) 100 101

/usr/lib/python3.8/ast.py in _convert(node) 86 if len(node.keys).= len(node:values), 87 _raise_malformed_node(node) ---> 88 return dict(zip(map(_convert. node,keys), 89 map(_convert. node,values))) 90 elif isinstance(node. BinOp) and isinstance(node,op, (Add: Sub)):

/usr/lib/python3.8/ast.py in _convert(node) 86 if len(node.keys).= len(node:values), 87 _raise_malformed_node(node) ---> 88 return dict(zip(map(_convert. node,keys), 89 map(_convert. node,values))) 90 elif isinstance(node. BinOp) and isinstance(node,op, (Add: Sub)):

/usr/lib/python3.8/ast.py in _convert(node) 86 if len(node.keys).= len(node:values), 87 _raise_malformed_node(node) ---> 88 return dict(zip(map(_convert. node,keys), 89 map(_convert. node,values))) 90 elif isinstance(node. BinOp) and isinstance(node,op, (Add: Sub)):

/usr/lib/python3.8/ast.py in _convert(node) 86 if len(node.keys).= len(node:values), 87 _raise_malformed_node(node) ---> 88 return dict(zip(map(_convert. node,keys), 89 map(_convert. node,values))) 90 elif isinstance(node. BinOp) and isinstance(node,op, (Add: Sub)):

/usr/lib/python3.8/ast.py in _convert(node) 86 if len(node.keys).= len(node:values), 87 _raise_malformed_node(node) ---> 88 return dict(zip(map(_convert. node,keys), 89 map(_convert. node,values))) 90 elif isinstance(node. BinOp) and isinstance(node,op, (Add: Sub)):

/usr/lib/python3.8/ast.py in _convert(node) 96 else: 97 return left - right ---> 98 return _convert_signed_num(node) 99 return _convert(node_or_string) 100

/usr/lib/python3.8/ast.py in _convert_signed_num(node) 73 else: 74 return - operand ---> 75 return _convert_num(node) 76 def _convert(node): 77 if isinstance(node, Constant):

/usr/lib/python3.8/ast.py in _convert_num(node) 64 def _convert_num(node): 65 if not isinstance(node, Constant) or type(node.value) not in (int, float, complex): ---> 66 _raise_malformed_node(node) 67 return node.value 68 def _convert_signed_num(node):

/usr/lib/python3.8/ast.py in _raise_malformed_node(node) 61 node_or_string = node_or_string.body 62 def _raise_malformed_node(node): ---> 63 raise ValueError(f'malformed node or string: {node:r}') 64 def _convert_num(node), 65 if not isinstance(node. Constant) or type(node,value) not in (int, float: complex):

ValueError: malformed node or string: <_ast.Name object at 0x7f17e20ca0a0>

here you go

lst = [{'diet': {'Diet 0': {'gender': 0,
   'nutrients': {'Alcohol': {'min': 0, 'max': 14, 'unit': 'oz'},
    'Caffeine': {'min': 0, 'max': 400, 'unit': 'mg'},
    'Copper': {'min': 0, 'max': 0, 'unit': 'mg'},
    'Calcium': {'min': 1000, 'max': 2500, 'unit': 'mg'},
    'Choline': {'min': 425, 'max': 3500, 'unit': 'mg'},
    'Cholesterol': {'min': 0, 'max': 300, 'unit': 'mg'},
    'Fluoride': {'min': 0, 'max': 0, 'unit': 'mg'},
    'SaturatedFat': {'min': 0, 'max': -1, 'unit': 'g'},
    'VitaminA': {'min': 2330, 'max': 10000, 'unit': 'IU'},
    'VitaminC': {'min': 75, 'max': 2000, 'unit': 'mg'},
    'VitaminD': {'min': 15, 'max': 100, 'unit': 'mcg'},
    'VitaminE': {'min': 15, 'max': 1000, 'unit': 'mg'},
    'VitaminK': {'min': 0, 'max': 0, 'unit': 'mcg'},
    'VitaminB1': {'min': 1.1, 'max': 0, 'unit': 'mg'},
    'VitaminB2': {'min': 0, 'max': 0, 'unit': 'mg'},
    'VitaminB5': {'min': 0, 'max': 0, 'unit': 'mg'},
    'VitaminB3': {'min': 0, 'max': 0, 'unit': 'mg'}, ............................]

for d in lst:
    d['Saturated Fat'] = d.pop('SaturatedFat')
print(lst)

You could use a recursive function that would change the key, wherever it occurred in the nested hierarchy:

Edited for a typo in the function defintions

>>> d = {'diet': {'Diet 0': {'gender': 0,
...:    'nutrients': {'Alcohol': {'min': 0, 'max': 14, 'unit': 'oz'},
...:     'Caffeine': {'min': 0, 'max': 400, 'unit': 'mg'},
...:     'SaturatedFat': {'min': 0, 'max': -1, 'unit': 'g'},
...:     'VitaminA': {'min': 2330, 'max': 10000, 'unit': 'IU'}}}}}

>>> def replace_key(d, old_key, new_key):
...:     for k, v in tuple(d.items()):
...:          if k == old_key:
...:              d[new_key] = d.pop(old_key)
...: 
...:          if isinstance(v, dict):
...:              replace_key(v, old_key, new_key)
...: 

>>> replace_key(d, old_key='SaturatedFat', new_key='Saturated Fat')
>>> d
{'diet': {'Diet 0': {'gender': 0,
   'nutrients': {'Alcohol': {'min': 0, 'max': 14, 'unit': 'oz'},
    'Caffeine': {'min': 0, 'max': 400, 'unit': 'mg'},
    'VitaminA': {'min': 2330, 'max': 10000, 'unit': 'IU'},
    'Saturated Fat': {'min': 0, 'max': -1, 'unit': 'g'}}}}}

This function changes the dictionary in-place, and returns None , but it is easily modified to return a copy, leaving the original dictionary unchanged:

>>> import copy
>>> def replace_key(d, old_key, new_key):
...:     d = copy.deepcopy(d)
...:     for k, v in tuple(d.items()):
...:          if k == old_key:
...:              d[new_key] = d.pop(old_key)
...: 
...:          if isinstance(v, dict):
...:              replace_key(v, old_key, new_key)
...: 
...:     return d

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM