Nam G VU
Nam G VU

Reputation: 35404

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

Upvotes: 1

Views: 66

Answers (3)

Nam G VU
Nam G VU

Reputation: 35404

If you can drop dpath, let's write ourself version mget_dict(d, key_path)

def mget_dict(d:dict, key_path, val_whnotfound=None):
  """
  ref https://g.co/gemini/share/06e7f2ced5d8
  recursively retrieves a value from a nested dictionary.
  """
  if not isinstance(key_path, list): return None

  if not key_path: return d

  key = key_path[0]
  if not d.get(key): return val_whnotfound
  else:              return mget_dict(d[key], key_path[1:], val_whnotfound)  # recursively go next level

Upvotes: 0

Kenny Ostrom
Kenny Ostrom

Reputation: 5871

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'}}}}

Upvotes: 1

user26979062
user26979062

Reputation: 21

The simple way using dpath is as show below where new is used to create or update the path in dict and path representation will be take care using root and zero padded keys are considered as string instead of integer.

import dpath.util

# Initialize an empty dictionary
data = {}

# Adding keys '00', '01', '02' as strings
keys = ["00", "01", "02"]
for key in keys:
    dpath.util.new(data, f"root/{key}", f"value_for_{key}")

# Display the resulting dictionary
print(data)

Upvotes: 1

Related Questions