Giacomo Casoni
Giacomo Casoni

Reputation: 427

Convert Python dictionary keys following a key-to-key mapping

I am in need of a function used to convert the keys of a Python dict to something else, according to a keys mapping. So for example let's say I have the mapping:

{
    "olk_key_1": "new_key_1",
    "olk_key_2": "new_key_2",
    "olk_key_3": "new_key_3",
}

And the dict:

{
    "old_key_1": 1,
    "old_key_2": 2,
    "old_key_3": 3,
}

What I want is:

{
    "new_key_1": 1,
    "new_key_2": 2,
    "new_key_3": 3,
}

The tricky part about this is that the functions MUST SUPPORT ANY SORT OF NESTED STRUCTURE.

That includes:

I currently have an ugly working function. Anything better looking (feel free to just refactor my code) will be considered as an answer.

def map_keys(self, data, mapping):
    """
    This function converts the data dictionary into another one with different keys, as specified by the mapping
    parameter
    :param data: The dictionary to be modified
    :param mapping: The key mapping
    :return: A new dictionary with different keys
    """
    new_data = data.copy()
    if isinstance(new_data, list):
        new_data = {"tmp_key": new_data}
        mapping.update({"tmp_key": "key_tmp"})
    iterate = list(new_data.items())
    for key, value in iterate:
        if isinstance(value, list) and isinstance(value[0], dict):
            new_list = []
            for item in new_data[key]:
                new_list.append(self.map_keys(item, mapping))
            new_data[mapping[key]] = new_list
        else:
            new_data[mapping[key]] = value
        new_data.pop(key)
    if "key_tmp" in new_data:
        new_data = new_data["key_tmp"]
    return new_data

EDIT

As an example, the function should be able to convert inputs such as (intentionally excessively convoluted):

[
    {
        "a": 1,
        "b":[
                {
                    "c": 1,
                    "d": 1
                },
                {
                    "e": 1,
                    "f": 1
                }
            ]
    },
    {
        "g": {
            "h": {
                "i": 1,
            },
            "j": {
                "k": 1  
            }
        }
    }
]

Upvotes: 2

Views: 1233

Answers (1)

Andrej Kesely
Andrej Kesely

Reputation: 195438

You can use json.load() with custom objects_pairs_hook parameter (doc):

mapping_d = {
    "old_key_1": "new_key_1",
    "old_key_2": "new_key_2",
    "old_key_3": "new_key_3",
}

d = {
    "old_key_1": 1,
    "old_key_2": 2,
    "old_key_3": [
        {"old_key_1": 4},
        {"old_key_2": 5}
    ],
}

import json

def fn(obj):
    rv = dict([(mapping_d.get(n, n), v) for n, v in obj])
    return rv

d = json.loads(json.dumps(d), object_pairs_hook=fn)
from pprint import pprint
pprint(d, width=30)

Prints:

{'new_key_1': 1,
 'new_key_2': 2,
 'new_key_3': [{'new_key_1': 4},
               {'new_key_2': 5}]}

Upvotes: 4

Related Questions