0

I'm curious how one would go about renaming duplicated key names in a Python dict containing potentially multiple nested dicts.

I have a dictionary that will be used to dynamically populate a UI. Each key's value is a list containing an identifier int at v[0], and depending on the identifier, subsequent items at v[n>0]. The general format of this dict is as follows:

MenuItems = {

    'Title': [0], #Text
    '---': [1], #Separator

    'MenuItem 1': [3, { #Dropdown Submenu

        'SubItem 1': [2, 1000], #Submenu Items that return a value
        '---': [1],
        'SubItem 2': [2, 2000],
        '---': [1],
        'SubItem 3': [2, 3000]

        }],

    '---': [1],
    'MenuItem2': [2, 4000]

    }

I've written a recursive function to then traverse this dictionary and add the different menu items to the UI, however unfortunately I overlooked the fact that each separator has the same key name. As a result when the UI is built, only the first separator is shown in each UI level corresponding to the dict's level, like so:

Title
---
MenuItem 1
    SubItem 1
    ---
    SubItem 2
    SubItem 3
MenuItem2

Interestingly, and (for me) surprisingly, it is the first separator that is displayed in each UI level. I was under the assumption that as the duplicated key names overwrite each other from top to bottom, that it would be the last separator that would be shown, however it seems as though the first key's position is maintained, however it inherits the values of subsequent duplicate keys.

From reading up on python dicts further I understand that they may not be the best structure to use for data that is order-dependent (as is a UI builder), however their legibility and ease of iteration via for k, v in dict.items() means that ideally I'd like to continue with them if possible, rather than swapping to a list-based structure.

As renaming keys in a dict that's being looped over gives an error, to tackle the issue I've instead tried recursively iterating through each key in the dict and adding it to a new empty dict. The idea behind this is that for each entry in the original dict, its k & v is copied to the new dict one at a time, and if one of the duplicated '---' keys is reached, its name is changed to 'Separator ' + a count number. I'd then be able to use this new dictionary with my original function. Unfortunately I haven't been able to get this working and although the keys are copied and renamed correctly, again only the first separator key from each nested dict is included. The function looks like this:

old_dict = {#The same dict as in the original code block above}
new_dict = {}
global_count = 1

def prepare_dict(old, new):

    global global_count

    for k, v in old.items():
        if v[0] == 0:
            new.update({k:v})

        elif v[0] == 1:
            new["Seperator %s" % global_count] = old[k]
            global_count += 1

        elif v[0] == 2:
            new.update({k:v})

        elif v[0] == 3:

            new_dict = {}
            prepare_dict(v[1], new_dict)
            new.update({k:(v[0], new_dict)})
        
        else: pass 


prepare_dict(old_dict, new_dict)
print(new_dict)

This results in (where the commented lines are what I'd expect/hope to be included):

{'Title': [0], 
'Seperator 1': [1], 
'MenuItem 1': (3, {
    'SubItem 1': [2, 1000], 
    'Seperator 2': [1], 
    'SubItem 2': [2, 2000],
    #'Seperator 3': [1],  
    'SubItem 3': [2, 3000]}),
#'Seperator 4': [1], 
'MenuItem2': [2, 4000]}

Any ideas or suggestions would be very much appreciated, I have looked at a number of similar questions but none have quite brought me to the right answer yet. It seems like there should be a way to go through the raw dictionary line-by-line only while looking at the key at the current iteration and without the duplicated keys affecting their predecessors, but obviously I'm open to recommendations if there really is no way to achieve what I'm trying to do with dicts! Thanks :)

Hexbob6
  • 167
  • 1
  • 2
  • 12
  • 4
    What you have can never be done in Python, see [thread](https://stackoverflow.com/questions/30648813/dict-with-same-keys-names). Consider using a list of 2-tuple instead. – metatoaster Jan 09 '23 at 01:23
  • 1
    Although a definition like `{1: 'a', 1: 'b'}` is syntactically valid in Python, it won't result in a dictionary that actually has the same key twice. It will only retain the latter value for the one key. – Grismar Jan 09 '23 at 01:26
  • 1
    There's no point of using a dict if you aren't going to use its keys to access the values. Use a list of tuples for sequential iterations. – blhsing Jan 09 '23 at 01:28
  • Do you have any control over modifying the structure of the `MenuItems` dict or the function that generates the `MenuItems` dict? Or your question here is how do you fix these dicts that you have saved somewhere already. A better question might be - how are you receiving this dict? Are you generating it? getting it as a json? etc. – Akshay Sehgal Jan 09 '23 at 01:30
  • I ask this because you mention at the start of the question `I'm curious how one would go about renaming duplicated key names in a Python dict`. The issue, as pointed out before, is that you cant store such a dict to begin with, and so if you fetched this dict from a source you have no control over, you have already lost the info to "fix" this. – Akshay Sehgal Jan 09 '23 at 01:34
  • 1
    The point is that there is no value in storing a dict with a duplicated key, since it would be then unclear which value to return when the dict is given that key for value retrieval. If you rename the duplicated key, it would also be pointless since you then aren't going to retrieve its value by a key you can't determine ahead of time without iterating through the entire set of keys, missing the whole point of a dict, which is to be able to access the values by keys in an average time complexity of *O(1)*. – blhsing Jan 09 '23 at 01:36

0 Answers0