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.
Example
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"]
},
"PURCHASEDGOODS": [
{
"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 = ""
FIELD_MAP = {
("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))
else:
result.append(val)
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?