pythondictionarydpath

Python how to set dict key as 01, 02, 03 ... when using dpath?


Let's say we want to create/maintain a dict with below key structure

{
  "a": {"bb": {"01": "some value 01", 
               "02": "some value 02", 
       } },
}    

We use dpath .new() as below to do this

import dpath

d=dict()                          ; print(d)  # d={}
dpath.new(d,'a/bb/00/c', '00val') ; print(d)  # d={'a': {'bb': [{'c': '00val'}]}}
#                                         d   # NOTE this will set :bb as a list, and set at bb[00] ie idx=0 of list bb
#                                         d   # what we want instead is d={'a': {'bb': {'00': {'c': '00val'} }}}
#                                         d   # what we want is NOT     d={'a': {'bb': [      {'c': '00val'} ]}}

So when using 00 in the path, dpath translate it into list index at 0 instead of dict key 00

The question is how to set key as 00? Currently I have to dodge it by setting prefix s ie s00 s01 s02


Solution

  • I traced into the source, and noticed that it calls a Creator, documented in the function as:

    creator allows you to pass in a creator method that is
    responsible for creating missing keys at arbitrary levels of
    the path (see the help for dpath.path.set)

    I copied _default_creator from dpath.segments, and just eliminated the code that treats int values as a list index. Here's the quick and dirty version:

    import dpath
    from dpath.segments import extend
    from typing import Sequence, MutableSequence
    
    def my_creator(current, segments, i, hints):
        segment = segments[i]
        length = len(segments)
    
        if isinstance(current, Sequence):
            segment = int(segment)
    
        if isinstance(current, MutableSequence):
            extend(current, segment)
    
        # Infer the type from the hints provided.
        if i < len(hints):
            current[segment] = hints[i][1]()
        else:
            # deleted the array stuff here
            current[segment] = {}
    
    d = dict()
    dpath.new(d, r'a/bb/00/c', '00val', creator=my_creator)
    print(d)
    

    output

    {'a': {'bb': {'00': {'c': '00val'}}}}