Home > Net >  Mapping of Keys using dictionary and lists in Python
Mapping of Keys using dictionary and lists in Python

Time:12-31

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 the mod

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)
  • Related