Given a Dict[str, Any] and a mapping of "key paths" to new names, produce a new dict in the shape of the original that uses the new names while preserving everything else.


Let's say this is the original dict:

d = {
    "id": 1,
    "summary": {
        "origin": {
            "url": "https://www.google.com/",
            "slug": "google"
        "tags": ["tag1", "tag2"]
    "items": [
            "id": "abc",
            "price": 50
            "id": "def",
            "price": 110,
            "discount": 50

The following should be produced:

newd = {
    "IDENTIF": 1,
    "SUMM": {
        "ORIG": {
            "WEBADDRESS": "http://www.google.com/",
            "LOCATOR": "google"
        "TAGS": ["tag1", "tag2"]
            "GOODSID": "abc",
            "GOODSPRICE": 50
            "GOODSID": "def",
            "GOODSPRICE": 110,
            "GIVENDISCOUNT": 50

It is important that if the original dict had been {"items": {"id": "123"}}, the produced dict must be {"PURCHASEDGOOD": {"id": "123"}} because in the required replacement scheme, "id" is replaced with "GOODSID" only if it occurs as a key of a dict that is an element of a list under "items".

My solution

The user of the code is supposed to call the mapped_to_fields function which is at the very bottom.

from typing import Tuple, Dict

# Maps a path in a dictionary (a key path) to a key name. For
# example, the mapping ("a", "b", "c") -> "d" maps the nested "c" in
# {"a": {"b": {"c": "this one"}}, "c": "not this one"} to a string
# "d". LIST denotes a list nested within a dict:
# ("one", LIST, "two") -> "x" will map every "two" in
# {"one": [{"two": "val1"}, {"two": "val2"}]} to "x".
LIST = ""
    ("id",):                        "IDENTIF",
    ("summary",):                   "SUMM",
    ("summary", "origin"):          "ORIG",
    ("summary", "origin", "url"):   "WEBADDRESS",
    ("summary", "origin", "slug"):  "LOCATOR",
    ("summary", "tags"):            "TAGS",
    ("items",):                     "PURCHASEDGOODS",
    ("items", LIST, "id"):          "GOODSID",
    ("items", LIST, "price"):       "GOODSPRICE",
    ("items", LIST, "discount"):    "GIVENDISCOUNT"

def mapped_to_fields_list(l: list, field_map: Dict[Tuple[str], str], parent: Tuple[str]) -> list:
    result = list()

    for val in l:
        if isinstance(val, dict):
            result.append(mapped_to_fields(val, field_map, parent))
        elif isinstance(val, list):
            result.append(mapped_to_fields_list(val, field_map, parent))

    return result

def mapped_to_fields(d: dict, field_map: Dict[Tuple[str], str], parent: Tuple[str] = ()) -> dict:
    result = dict()

    for key, val in d.items():
        key_path = parent + (key,)
        replacement_key = field_map.get(key_path, key)

        mapped_val = val
        if isinstance(val, dict):
            mapped_val = mapped_to_fields(val, field_map, key_path)
        elif isinstance(val, list):
            mapped_val = mapped_to_fields_list(val, field_map, key_path + (LIST,))

        result[replacement_key] = mapped_val

    return result
  • Is the algorithm optimal?
  • Does the code adhere to Python standards?
  • Is there a better way to encode the replacement scheme?
  • 1
    \$\begingroup\$ This problem statement looks suspiciously similar to one that was posed in another recent question. Are you two related somehow? \$\endgroup\$ Commented Jun 22, 2022 at 7:07
  • \$\begingroup\$ @200_success I had found this question on reddit originally, and wanted to have my solution reviewed. \$\endgroup\$
    – csmathhc
    Commented Jun 22, 2022 at 8:50
  • \$\begingroup\$ Can you link the Reddit post? \$\endgroup\$
    – Reinderien
    Commented Jun 22, 2022 at 13:29
  • \$\begingroup\$ @Reinderien Unfortunately, it seems to have been deleted. \$\endgroup\$
    – csmathhc
    Commented Jun 22, 2022 at 17:41
  • 1
    \$\begingroup\$ That's one of the reasons questions should be self-contained whenever possible. \$\endgroup\$
    – Mast
    Commented Jun 23, 2022 at 6:18


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.