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\$ Jun 22 at 7:07
  • \$\begingroup\$ @200_success I had found this question on reddit originally, and wanted to have my solution reviewed. \$\endgroup\$
    – csmathhc
    Jun 22 at 8:50
  • \$\begingroup\$ Can you link the Reddit post? \$\endgroup\$
    – Reinderien
    Jun 22 at 13:29
  • \$\begingroup\$ @Reinderien Unfortunately, it seems to have been deleted. \$\endgroup\$
    – csmathhc
    Jun 22 at 17:41
  • 1
    \$\begingroup\$ That's one of the reasons questions should be self-contained whenever possible. \$\endgroup\$
    – Mast
    Jun 23 at 6:18


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.