I have a list of list of dictionaries like below:
ini_dict = [[
{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 16},
{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 15}
]]
Step 1: Reduce one level
I have modified and reduced one level like below:
mod = []
for i in ini_dict:
for j in i:
mod.append({item: values for item, values in j.items()})
This gives me output as below:
[
{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 15},
{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 15}
]
Step 2: Replace the keys with values from another dict
Now, I have another set of keys dictionary result_dict1
where I use the keys (here in lowercase) to replace the keys from above data mod
with related values (here in uppercase).
result_dict1 = {
"nikhil": "Nikhil",
"vashu": "Vashu",
"manjeet": "Manjeet",
"akshat": "Akshat"
}
Code I tried
Now, I have tried the below code but it's not giving me the output I need:
final = []
for j, k in result_dict1.items():
for i in mod:
for x, y in i.items():
final = [{k: y}]
Output:
[{'Nikhil': 1}, {'Nikhil': 5}, {'Nikhil': 10}, {'Nikhil': 15}, {'Nikhil': 1}, {'Nikhil': 5}, {'Nikhil': 10}, {'Nikhil': 15}, {'Vashu': 1}, {'Vashu': 5}, {'Vashu': 10}, {'Vashu': 15}, {'Vashu': 1}, {'Vashu': 5}, {'Vashu': 10}, {'Vashu': 15}, {'Manjeet': 1}, {'Manjeet': 5}, {'Manjeet': 10}, {'Manjeet': 15}, {'Manjeet': 1}, {'Manjeet': 5}, {'Manjeet': 10}, {'Manjeet': 15}, {'Akshat': 1}, {'Akshat': 5}, {'Akshat': 10}, {'Akshat': 15}, {'Akshat': 1}, {'Akshat': 5}, {'Akshat': 10}, {'Akshat': 15}]
Problem
Keys are replaced but the combination is 1:N
.
Expected output:
[
{'Nikhil': 1, 'Vashu': 5, 'Manjeet': 10, 'Akshat': 15},
{'Nikhil': 1, 'Vashu': 5, 'Manjeet': 10, 'Akshat': 15}
]
CodePudding user response:
Issues
You were almost there:
- Step 1: works as expected
- Step 2: iterates over the key-mapping
result_dict1
instead over themod
Fixed
I added comments where I
- removed the loop or added it back slightly modified
- introcuced an inner collection (dict) to keep the source separation
ini_dict = [[
{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 16},
{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 15}
]]
# step 2:
mod = []
for i in ini_dict:
for j in i:
mod.append({item: values for item, values in j.items()})
print(f"step 1:\n{mod}")
# step 2:
result_dict1 = {
"nikhil": "Nikhil",
"vashu": "Vashu",
"manjeet": "Manjeet",
"akshat": "Akshat"
}
final = []
# for j, k in result_dict1.items():
for i in mod:
inner = {} # collect inner separately
for x, y in i.items():
k = result_dict1[x] # lookup in dict, instead for-loop
# final = [{k: y}]
inner[k] = y # add to inner (not to final outer) !as dict-entry! (not as list)
final.append(inner) # keep separation
print(f"step 2:\n{final}")
Prints:
step 1:
[{'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 16}, {'nikhil': 1, 'vashu': 5, 'manjeet': 10, 'akshat': 15}]
step 2:
[{'Nikhil': 1, 'Vashu': 5, 'Manjeet': 10, 'Akshat': 16}, {'Nikhil': 1, 'Vashu': 5, 'Manjeet': 10, 'Akshat': 15}]
Note:
Your original outer for-loop k for j, k in result_dict1.items()
can be rewritten as:
keys = [k for j, k in result_dict1.items() if j == x] # for-loop as list-comprehension with added conditional
k = keys[0] # key expected as first element in resulting list
Pretty complicated.. plus, we expect to find the key, but what if the list keys
is empty? Then keys[0]
would raise IndexError: list index out of range
.
Better we benefit from the dict here and simplify the lookup: k = result_dict1[x]
Advice: Naming helps
As far as I can see, the naming of variables can be improved. Then maybe this logical flaw would have been recognized earlier.
For example see the inner
is more meaningful than i
. It can be read and understood like human language - without keeping a name-dictionary or mapping like "i: inner list or dictionary" in memory as additional cognitive load.
Fixed alternative
Compare the current implementation for Step 2 with yours from Step 1:
mod = []
for i in ini_dict:
for j in i:
mod.append({item: values for item, values in j.items()})
print(f"step 1:\n{mod}")
We can reuse the pattern known as dict comprehension here too:
final = []
for i in mod:
final.append({result_dict1[x]: values for x, values in i.items()})
print(f"step 2:\n{final}")
Benefits:
- visual resemblance can be identified as shape (even when zoomed-out to 10% in large files)
- it looks simpler because familiar (after we already understood step 1)
- easier recognize which parts are similar (to step 1) and where they differ (e.g. no nested level for-loop)
This is also called code symmetry. See e.g. Carlos Ble (2014): Code Symmetry
CodePudding user response:
You don't seem to check if a key of the inner dictionary in mod
matches the key in the replacement dict, that's an issue.
A list comprehension with a dict comprehension does the result:
>>> [{result_dict1[key]: value for key, value in inner_dict.items()}
for inner_dict in mod]
[{'Nikhil': 1, 'Vashu': 5, 'Manjeet': 10, 'Akshat': 15},
{'Nikhil': 1, 'Vashu': 5, 'Manjeet': 10, 'Akshat': 15}]
So the dict comprehension is changing the inner dicitionaries of the mod
such that the keys are replaced from result_dict1
. The list comprehension helps do this for every inner dictionary.
This will err if result_dict1
isn't comprehensive enough.
The comprehensions above can be unwrapped as
# this is the final result; we'll fill in this
end_list = []
# foreach dictionary in `mod`...
for inner_dict in mod:
# define anew empty dict
new_inner_dict = {}
# foreach (key, val) pair of the `inner_dict`...
for key, value in inner_dict.items():
# get the `new_key` from the replacer mapping
new_key = result_dict1[key]
# put it into the new dict (but the value is the same)
new_inner_dict[new_key] = value
# above for ended, we have a new_inner_dict; save it
end_list.append(new_inner_dict)
print(end_list)