Reputation: 665
I have two dict, dict1 and dict2, I want to construct new dict(or manipulate dict1) with key-value pairs as(value of dict2:value of dict1 [where key of dict one and key of dict2 are same]) , value of a key may be list of dicts(as you will see in input example)
INPUT IS
dict1 = {"key1":{"key3":"value1","key2":"value2","key4":{"key5":"value3","key6":{"key7":"value4","key8":{"key9":"value5","key10":"value6","key55":"value7"}},"key11":{"key12":"value8","key13":"value9"},"key14":[{"key15":"value10","key16":"value11","key17":"value12"},{"key15":"value13","key16":"value14","key17":"value15"}]}}}
dict2 = {"key1":"ab","key2":"bc","key3":"cd","key4":"de","key5":"ef","key6":"fg","key7":"gh","key8":"hi","key9":"ij","key10":"jk","key55":"kl","key11":"lm","key12":"mn","key13":"no","key14":"op","key15":"pq","key16":"qr","key17":"qs"}
My function is
def walk(dict1, dict2):
output = {}
for key, value in dict1.iteritems():
if isinstance(value, dict):
output[dict2[key]] = walk(value, dict2)
elif isinstance(value, list):
output[dict2[key]] = walk_list(value, dict2)
else:
output[dict2[key]] = value
return output
def walk_list(sublist, dict2):
output = []
for i in sublist:
if isinstance(i, dict):
output = walk(i, dict2)
elif isinstance(value, list):
output = walk_list(i, dict2)
else:
output.append((key, value))
return output
output = walk(dict1, dict2)
output = json.dumps(output)
print output
OUTPUT AM GETTING IS
{"ab": {"de": {"lm": {"mn": "value8", "no": "value9"}, "ef": "value3", "fg": {"hi": {"ij": "value5", "jk": "value6", "kl": "value7"}, "gh": "value4"}, "op": {"pq": "value13", "qs": "value15", "qr": "value14"}}, "bc": "value2", "cd": "value1"}}
EXPECTED OUTPUT IS
{"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}
Please fix my code.
Upvotes: 4
Views: 1264
Reputation: 387557
Very simple solution that performs the recursive step very early and as such has a very simple logic:
def translateKeys (obj, keyNames):
if isinstance(obj, dict):
return {keyNames.get(k, k): translateKeys(v, keyNames) for k, v in obj.items()}
elif isinstance(obj, list):
return [translateKeys(v, keyNames) for v in obj]
else:
return obj
Instead of expecting a certain type, it just accepts anything (dictionary, list, or whatever) and works on the items of it, calling itself for every value. This avoids having to iterate through obj
itself and check the value of each item within the loop.
Used on your example data:
>>> dict1 = {"key1":{"key3":"value1","key2":"value2","key4":{"key5":"value3","key6":{"key7":"value4","key8":{"key9":"value5","key10":"value6","key55":"value7"}},"key11":{"key12":"value8","key13":"value9"},"key14":[{"key15":"value10","key16":"value11","key17":"value12"},{"key15":"value13","key16":"value14","key17":"value15"}]}}}
>>> dict2 = {"key1":"ab","key2":"bc","key3":"cd","key4":"de","key5":"ef","key6":"fg","key7":"gh","key8":"hi","key9":"ij","key10":"jk","key55":"kl","key11":"lm","key12":"mn","key13":"no","key14":"op","key15":"pq","key16":"qr","key17":"qs"}
>>> expected = {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}
>>> result = translateKeys(dict1, dict2)
>>> result
{'ab': {'de': {'fg': {'gh': 'value4', 'hi': {'ij': 'value5', 'jk': 'value6', 'kl': 'value7'}}, 'op': [{'qr': 'value11', 'pq': 'value10', 'qs': 'value12'}, {'qr': 'value14', 'pq': 'value13', 'qs': 'value15'}], 'ef': 'value3', 'lm': {'no': 'value9', 'mn': 'value8'}}, 'cd': 'value1', 'bc': 'value2'}}
>>> result == expected
True
If you want to invert this translation, you can just invert keyNames
and perform the translation on the result instead:
>>> result = translateKeys(dict1, dict2)
>>> invertedKeyNames = {v: k for k, v in dict2.items()}
>>> original = translateKeys(result, invertedKeyNames)
>>> original == dict1
True
Upvotes: 4
Reputation: 11590
I think this solves the riddle using one function only
def walk(dict1, dict2):
res = dict()
for k,v in dict1.items():
if isinstance(v,list):
newv = [walk(x, dict2) for x in v]
elif isinstance(v,dict):
newv = walk(v, dict2)
else:
newv = v
res[dict2.get(k, k)] = newv # keep the same key if not present in dict2
return res
expected = {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}
output = walk(dict1, dict2)
print(output)
print(output == expected)
as it produces
{'ab': {'de': {'lm': {'no': 'value9', 'mn': 'value8'}, 'ef': 'value3', 'fg': {'hi': {'ij': 'value5', 'kl': 'value7', 'jk': 'value6'}, 'gh': 'value4'}, 'op': [{'qr': 'value11', 'qs': 'value12', 'pq': 'value10'}, {'qr': 'value14', 'qs': 'value15', 'pq': 'value13'}]}, 'cd': 'value1', 'bc': 'value2'}}
True
Basically it examines every value in the dictionary:
EDIT:
in case the input dictionaries are not only dictionaries after all, but can be any acceptable json element (e.g. list, dict, value) that can become more generalised
def walk(obj, keys):
if isinstance(obj,list):
return [walk(x, keys) for x in obj]
elif isinstance(obj,dict):
return {keys.get(k, k): walk(v, keys) for k,v in obj.items()}
else:
return obj
which is exactly what @Poke had answered from the beginning, kudos to him.
EDIT2:
in case you want to revert back to the original dictionary dict1
, provided the values are all disjoint (i.e. the dict2
mapping is a bijective function), you can do
back2dict1 = walk(output, {v:k for k,v in dict2.items()})
print(back2dict1)
print(back2dict1 == dict1)
which produces
{'key1': {'key3': 'value1', 'key2': 'value2', 'key4': {'key5': 'value3', 'key11': {'key12': 'value8', 'key13': 'value9'}, 'key14': [{'key15': 'value10', 'key16': 'value11', 'key17': 'value12'}, {'key15': 'value13', 'key16': 'value14', 'key17': 'value15'}], 'key6': {'key7': 'value4', 'key8': {'key10': 'value6', 'key55': 'value7', 'key9': 'value5'}}}}}
True
Upvotes: 1
Reputation: 1539
I think it is to do with your walk_list function with the output variable being assigned rather than appended to. This is my version:
dict1 = {"key1":{"key3":"value1","key2":"value2","key4":{"key5":"value3","key6":{"key7":"value4","key8":{"key9":"value5","key10":"value6","key55":"value7"}},"key11":{"key12":"value8","key13":"value9"},"key14":[{"key15":"value10","key16":"value11","key17":"value12"},{"key15":"value13","key16":"value14","key17":"value15"}]}}}
dict2 = {"key1":"ab","key2":"bc","key3":"cd","key4":"de","key5":"ef","key6":"fg","key7":"gh","key8":"hi","key9":"ij","key10":"jk","key55":"kl","key11":"lm","key12":"mn","key13":"no","key14":"op","key15":"pq","key16":"qr","key17":"qs"}
def walk(dict1, dict2):
output = {}
for key, value in dict1.iteritems():
if isinstance(value, dict):
outVal = walk(value, dict2)
elif isinstance(value, list):
outVal = walk_list(value, dict2)
else:
outVal = value
output[dict2[key]] = outVal
return output
def walk_list(sublist, dict2):
output = []
for i in sublist:
if isinstance(i, dict):
outVal = walk(i, dict2)
elif isinstance(i, list):
outVal = walk_list(i, dict2)
else:
outVal = i
output.append(outVal)
return output
mine = walk(dict1, dict2)
expecting = {"ab":{"cd":"value1","bc":"value2","de":{"ef":"value3","fg":{"gh":"value4","hi":{"ij":"value5","jk":"value6","kl":"value7"}},"lm":{"mn":"value8","no":"value9"},"op":[{"pq":"value10","qr":"value11","qs":"value12"},{"pq":"value13","qr":"value14","qs":"value15"}]}}}
print mine == expecting
Upvotes: 1