Change keys of a dict with a mapping dict

2020-08-01 03:24发布

I want to replace keys name of a dictionary by passing a mapping dict with a function that replace also nested keys. The issue is that I have multiple keys named 'id' in nested dictionary and I want to rename these 'id' with specific names.

Initial dictionnary:

initial_dict = {'id': 1, 'netAmount': 10.2, 'modifiedOn': '2017-01-01',
                    'statusId': 3, 'approvalStateId': 3, 'approvalState': {'id': 3,'name':'Approved'}}

Mapping dict:

 mapping_dict = {'id': 'pr_id', 'netAmount': 'net_amount', 'modifiedOn': 'modified_date',
                'statusId': 'status_id', 'approvalStateId': 'approval_id','approvalState':{'id':'approv_id'}}

Desired output of the dictionary:

    output_dict = {'pr_id': 1, 'net_amount': 10.2, 'modified_date': '2017-01-01',
                    'status_id': 3, 'approval_id': 3, 'approvalState': {'approv_id': 3, 'name': 'Approved'}}

What I did is this but it only replace keys of the first level of the dict and if I try to set nested keys in the mapping dict, I get an error.

def map_fields(obj):
    new_obj = {}
    mapping_dict = {'id': 'pr_id', 'netAmount': 'net_amount', 'modifiedOn': 'modified_date',
                    'statusId': 'status_id', 'approvalStateId': 'approval_id','approvalState':{'id':'approv_id'}}
    for key in obj.keys():
        if key in mapping_dict:
            new_key = mapping_dict[key]
        else:
            new_key = key
        new_obj[new_key] = obj[key]
    return new_obj

Do you have any idea how to do it?

Thanks

2条回答
一夜七次
2楼-- · 2020-08-01 03:49

You need a recursive function to be able to step up through the nested dictionaries. The key is that when recursing, you need to pass both the child dictionary, and the child mapping dictionary.

Note that the structure of your mapping dict is specific to this problem, and doesn't allow you to change the key of a nested dictionary - you'd need to restructure how you store the mapping to achieve that.

The following should do what you want (print statements added to help follow the logic when it runs):

def map_fields(init_dict, map_dict, res_dict=None):

    res_dict = res_dict or {}
    for k, v in init_dict.items():
        print("Key: ", k)
        if isinstance(v, dict):
            print("value is a dict - recursing")
            v = map_fields(v, map_dict[k])
        elif k in map_dict.keys():
            print("Remapping:", k, str(map_dict[k]))
            k = str(map_dict[k])
        res_dict[k] = v
    return res_dict

print(map_fields(initial_dict, mapping_dict))
查看更多
在下西门庆
3楼-- · 2020-08-01 03:54

Also if you want the same result but do not want to use a nested mapping dict such as :

mapping_dict = {'id': 'pr_id', 'netAmount': 'net_amount', 'modifiedOn': 'modified_date',
            'statusId': 'status_id', 'approvalStateId': 'approval_id', 'id':'approv_id'}

(ie 'id'/'approv_id' is not nested in 'approvalState')

You can use the slightly different function:

def map_fields(init_dict, map_dict, res_dict=None):
    res_dict = res_dict or {}
    for k, v in init_dict.items():
        if isinstance(v, dict):
            v = map_fields(v, map_dict)
        if k in map_dict.keys():
            k = str(map_dict[k])
        res_dict[k] = v
    return res_dict

This can be especially useful if the key 'approvalState' was also to map (to something like 'resultState', ie if your result needed to be:

output_dict = {'pr_id': 1, 'net_amount': 10.2, 'modified_date': '2017-01-01',
     'status_id': 3, 'approval_id': 3, 'resultState': {'approv_id': 3, 'name': 'Approved'}}

In which case, you would just have had to add the key/value pair 'approvalState'/'resultState' in your mapping dict.

查看更多
登录 后发表回答