2

There are three different nested dictionaries:

dict1={'g1': {'s1': 'checked', 's2': '0', 'Both':'checked'}, 'g2': {'s1': '0', 's2':'checked', 'Both': 'checked'}, 'g3': {'s1': 'checked', 's2': 'checked', 'Both': 'checked'}}
dict2={'g1': {'s1': '11', 's2': '', 'Both': '13'}, 'g2': {'s1': '', 's2': '22', 'Both':'23'}, 'g3': {'s1':'31', 's2': '32', 'Both': '33'}}
dict3={"s1": {"PS":'value1'}, "s2": {"PS": 'value2'}, "Both": {"PS": 'value3'}}

Desired nested dictionary is dict4. (Note: In format of dict1 or dict2, i.e. {'g1': {'s1': "xxxx",'s2':'xxxy'}}, where xxxx and xxxy are values from below given condition.)

Description: Keys and values are common approx. and the desired output required from above three dictionaries will also must be a nested dictionary having condition if value=='checked' in dict1 then dict3 with the new key 'Msg' and value from dict2 that is having same nested key must added to the desired nested dictionary dict4 having parent key from dict1.

My code:

dict4={}  # declaring desired dictionary
for nkey,nvalue in dict1.items():
    tempDict={} #temporary dictionary to store all checked from dict1
    for key,value in nvalue.items():
        if value=='checked':
            tempDict[key]=dict3[key]
            tempDict[key]['Msg']=dict2[nkey][key]
    #in prescribed format assigning dict4
    dict4[nkey]=tempDict

Wrong Output from above code: (wrong 'Msg' values)

dict4 = {'g1': {'s1': {'PS': 'value1', 'Msg': '31'}, 'Both': {'PS': 'value3', 'Msg': '33'}}, 'g2': {'s2': {'PS': 'value2', 'Msg': '32'}, 'Both': {'PS': 'value3', 'Msg': '33'}}, 'g3': {'s1': {'PS': 'value1', 'Msg': '31'}, 's2': {'PS': 'value2', 'Msg': '32'}, 'Both': {'PS': 'value3', 'Msg': '33'}}}

Correct and desired output: (with correct 'Msg')

dict4 = {'g1': {'s1': {'PS': 'value1', 'Msg': '11'}, 'Both': {'PS': 'value3', 'Msg': '13'}}, 'g2': {'s2': {'PS': 'value2', 'Msg': '22'}, 'Both': {'PS': 'value3', 'Msg': '23'}}, 'g3': {'s1': {'PS': 'value1', 'Msg': '31'}, 's2': {'PS': 'value2', 'Msg': '32'}, 'Both': {'PS': 'value3', 'Msg': '33'}}}

As per the code above, I am trying to store all checked from dict1 to a temporary dictionary, then adding the key 'Msg' (new key) and value from dict2 then assigning dict4 in desired format (key from dict1 and value from temporary dictionary i.e. tempDict).

Issue: As I observed is that, it over writes the 'Msg' from desired dict4 and tempDict values when changing then it reflects dict4 values while inside for loop (as I have debug the code by printing values in all stages).

Please clarify and suggest the possible solution and why when the value is changing on tempDict then it reflects to the already assigned to the previous key value in dict4, i.e. when tempDict changes 'Msg' for g3 then it changes values in g1 and g2 also.

I have tried tempDict.clear() method in place of tempDict={} then dict4 output is like:

dict4={}  # declaring desired dictionary
for nkey,nvalue in dict1.items():
    tempDict={} #temporary dictionary to store all checked from dict1
    for key,value in nvalue.items():
        if value=='checked':
            tempDict[key]=dict3[key]
            tempDict[key]['Msg']=dict2[nkey][key]
    #in prescribed format assigning dict4
    dict4[nkey]=tempDict
    tempDict.clear()  

Output from above: (wrong)

dict4 = {'g1': {}, 'g2': {}, 'g3': {'s1': {'PS': 'value1', 'Msg': '31'}, 's2': {'PS': 'value2', 'Msg': '32'}, 'Both': {'PS': 'value3', 'Msg': '33'}}},

Is there any memory assigned from value issue or somewhere I wrote wrong logic?

Correct and desired output: (with correct 'Msg')

dict4 = {'g1': {'s1': {'PS': 'value1', 'Msg': '11'}, 'Both': {'PS': 'value3', 'Msg': '13'}}, 'g2': {'s2': {'PS': 'value2', 'Msg': '22'}, 'Both': {'PS': 'value3', 'Msg': '23'}}, 'g3': {'s1': {'PS': 'value1', 'Msg': '31'}, 's2': {'PS': 'value2', 'Msg': '32'}, 'Both': {'PS': 'value3', 'Msg': '33'}}}

Code 3: (same output)

dict4={}
for nkey,nvalue in dict1.items():                 
  for key,value in nvalue.items():                        
    if value=='checked':
      try:
        dict4[nkey][key]=dict3[key]
      except:
        dict4[nkey]={}
      dict4[nkey][key]=dict3[key]
      dict4[nkey][key]['Msg']=dict2[nkey][key]
finefoot
  • 9,914
  • 7
  • 59
  • 102
Shiv Sr
  • 33
  • 6

1 Answers1

1

The issue is with Python's dict type being a mutable type. With tempDict[key]=dict3[key], you will put "a reference" to the already existing dict3[key] into tempDict[key]. If you change the object by accessing it via its reference from either dict, those changes will be "visible" everywhere (as both dict3[key] and tempDict[key] refer to the same object).

In other words, you access the "inner" dict via its reference in either "outer" dict so any modification which has been applied to the "inner" dict will be "visible" whichever way you use to access it. It's only one and the same object after all. After copy, you have two objects (two "inner" dict) and the two "outer" dict refer to each one of them.

More information about mutable/immutable types in Python: Immutable vs Mutable types, for example.

Regarding a solution: Good news, if you don't want both dict3[key] and tempDict[key] to refer to the same object, you can create a copy via the dict.copy method. So in your first code, the only thing you will have to change is

tempDict[key]=dict3[key]

to

tempDict[key]=dict3[key].copy()

and you'll get your desired output.

Should your dict contain further mutable types, that you want to change, you will have to create a copy of each "layer" with copy.deepcopy, for example.

finefoot
  • 9,914
  • 7
  • 59
  • 102
  • 1
    "he issue is with Python's dict type being a mutable type. Therefore, you won't put the keys and values into your new dict4 but the object itself." The mutability is irrelevant here, the same thing happens with immutable types. Immutable types *simply lack mutator methods*. – juanpa.arrivillaga Dec 16 '19 at 03:30
  • 1
    Yes, but only because you cannot mutate integers. But the semantics are still the same, the actual integer object and the actual dict object go in as values. *You just can't change an integer*. IOW, it's always the case that "only a reference" to the object goes in, whether or not that object is mutable or immutable. – juanpa.arrivillaga Dec 16 '19 at 03:34
  • @finefoot Thanks a lot,it really helps me to get the desired output. But there is a doubt arises ,as dictionaries are mutable then how it is pointing to the same reference( or id) of that object while iteration within the 'tempDict'. – Shiv Sr Dec 16 '19 at 13:44
  • @juanpa.arrivillaga does it means that when I was assigning dictionary as a value to dict4 from tempDict ,then it is only assigning the reference? then how we can say it is mutable? – Shiv Sr Dec 16 '19 at 13:47
  • @finefoot much clearer now ! I was not aware about this before. Thank You :) – Shiv Sr Dec 16 '19 at 16:26
  • @ShivSr I dont really understand your question I think, though, that you should read the following: https://nedbatchelder.com/text/names.html it should be illuminating, and it's very clear – juanpa.arrivillaga Dec 16 '19 at 17:17