Wizard
Wizard

Reputation: 22093

Replace elements in a nested dict with the appropriate in a list

I have such following a dict and a list.

mylist= ['1H1.PyModule.md',
         '1H2.Class.md',
         '1H3.MetaObject.md',
         '2B1D0.Data.md',
         '2B1D1.Primitive.md',
         '2B1D2.Operator.md',
         '2B2D3.Container.md',
         '2B2S0.Function.md',
         '2B2S0.Statemment.md',
         '2B2S1.Controlled_Loop.md',
         '2B2S2.Conditions.md',
         '2B2S3.Except.md',
         ...
         ]
 mydict = {'Body': {'Data': ['1.primitive', '2.operator', '3.container'],
   'Statement': ['0.function', '1.controlled_loop', '2.condition', '3.except']},
   'Header': ['1.Modle', '2.Class', '3.Object'],
   ...}

I attempt to repalce the strings in mydict with the appropriate in mylist

I can figure out '2B1D0.Data.md' has the shortest length, So I slice the keyward 'Data'

In [82]: '2B1D0.Data.md'[-7:-3]
Out[82]: 'Data'

The dict has both a nested list and nested dict.
So I write a iteration function with type checking
if an item's value isinstance(value,list), renew that value,
if an item's value isinstance(value, dict),call the function replace_ele()to continue.

I name string in mylist as str_of_list, while string in mydict as str_of_dict for readable concerns.

#replace one string in mydict
def replace_ele(mydict, str_of_list):
    for key, value in mydict.items():
        if isinstance(value, list): #type checking
            for str_of_dict in value:
                #replace str_of_dict with str_of_list
                if str_of_list[-7:-3].lower() == str_of_dict[-4:]: #[-7:-3] the shortest length
                    value.remove(str_of_dict)
                    value.append(str_of_list)
                    value.sort()
                    mydict[key] = value
        #iteration if a dict
        if isinstance(value, dict):
            replace_ele(value,str_of_list)

for str_of_list in mylist:
    replace_ele(mydict, str_of_list)

Then running and outputs:

Out[117]:
{'Body': {'Data': ['2B1D1.Primitive.md',
   '2B1D2.Operator.md',
   '2B2D3.Container.md'],
  'Statement': ['2B2S0.Function.md',
   '2B2S0.Function.md',
   '2B2S1.Controlled_Loop.md',
   '2B2S3.Except.md']},
 'Header': ['1.Modle', '1H2.Class.md', '1H3.MetaObject.md']
 ....}

I assume that such a problem can be solved with less codes. However, I cannot find that solution with the limited knowledge.

How to accomplish it elegantly?

Upvotes: 1

Views: 110

Answers (1)

fferri
fferri

Reputation: 18940

My suggestion is that you create a function to reduce element of mylist and elements in lists of mydict values to the same format:

For example, you can split by '.' character, take the second field, convert it to lower case:

def f(s):
    return s.split('.')[1].lower()

E.g.:

>>> f('2B2S1.Controlled_Loop.md')
'controlled_loop'
>>> f('1.controlled_loop')
'controlled_loop'

Now, from mylist create a dict to hold replacements:

mylist_repl={f(x): x for x in mylist}

i.e. mylist_repl contains key: value items such as 'metaobject': '1H3.MetaObject.md'.

With dict mylist_repl and function f, it is easy to transform a list from mydict to the desired value, example:

>>> [mylist_repl[f(x)] for x in ['1.primitive', '2.operator', '3.container']]
['2B1D1.Primitive.md', '2B1D2.Operator.md', '2B2D3.Container.md']

Also note that this dictionary lookup is more efficient (i.e.: faster) than a nested for loop!

If you have different replacement logic, you probably only need to change how f maps items from two different sets to a common key.

Upvotes: 2

Related Questions