Reputation: 33
I am giving the user the ability to check a specific key in a multi-level dictionary. My idea is that they will pass the path to the key like this:
root.subelement1.subelement2.key
This can be of arbitrary length and depth.
Once I have the string (above) from the user, I'll split it and get a list of each individual component:
elements = ['root', 'subelement1', 'subelement2', 'key']
All of this I can do. The next part is where I am stuck. How can I query the dictionary key, specified by the above when it's arbitrary length?
My initial thought was to do something like my_dict[elements[0]][elements[1]]
...but that doesn't scale or work when my user doesn't pass exactly the length I expect.
How can I get the data at an arbitrary key depth, in this case?
A couple examples:
country.US.NewYork
=> I query `my_dict['country']['US']['NewYork']department.accounting
=> I query my_dict['department']['accounting']
id
=> I query my_dict['id']
district.District15.HenryBristow.principal
=> I query my_dict['district']['District15']['HenryBristow']['principal']
Upvotes: 3
Views: 1415
Reputation: 140168
you could do that using reduce
which will query the keys in the nested dictionaries:
q = "district.District15.HenryBristow.principal"
my_dict = {"district" : {"District15" : {"HenryBristow" : {"principal" : 12}}}}
from functools import reduce # python 3 only
print(reduce(lambda x,y : x[y],q.split("."),my_dict))
result:
12
If you want to avoid to catch KeyError
in case the data doesn't exist with this path, you could use get
with a default value as empty dictionary:
reduce(lambda x,y : x.get(y,{}),q.split("."),my_dict)
Trying to get an unknown value returns an empty dictionary. The only drawback is that you don't know from where exactly the path got lost, so maybe leaving the KeyError
be raised wouldn't be so bad:
try:
v = reduce(lambda x,y : x[y],q.split("."),my_dict)
except KeyError as e:
print("Missing key: {} in path {}".format(e,q))
v = None
Upvotes: 6
Reputation: 505
u can do this like below
my_dict = someDict
tmpDict = dict(someDict) # a coppy of dict
input = "x.u.z"
array = input.split(".")
for key in array:
tmpDict = tmpDict[key]
print(tmpDict)
but your question is very challenging:
u say if user send country.us then go to my-dict.country.us
but what happen if one of this path in my_dict be a list code will results error u can handle this by check type
if isinstance(tmpDict , dict ):
tmpDict = tmpDict[key]
else:
# u should say what u want else (a Recursive method u will need)
edit
if user address maybe wrong you should check my_dict
have this field or not sample code is below but will be many if i don't like that!
if key not in tmpDict:
print("Bad Path")
return
Upvotes: 0
Reputation: 474
Use recursion. Ex:
root = {
'subelement1': {
'subelement2': {
'key': 'value'
}
}
}
elements = ['subelement1', 'subelement2', 'key']
def getElem(d, keys):
if keys == []:
return None
else:
key = keys[0]
remainingKeys = keys[1:]
if remainingKeys == []:
return d[key]
else:
if type(d[key]) == dict:
return getElem(d[key], remainingKeys)
else:
return None
print(getElem(root, elements))
Upvotes: 2
Reputation: 71451
You can transverse the dictionary using a for loop:
s = 'root.subelement1.subelement2.key'
d1 = {'root':{'subelement1':{'subelement2':{'key':15, 'key1':18}}}}
new_d = d1
for key in s.split('.'):
new_d = new_d[key]
print(new_d)
Output:
15
Upvotes: 0
Reputation: 1
from a python 2.x perspective, you can do this with reduce.
query_list = keys.split(":")
print reduce(lambda x,y: x[y], [my_dict] + query_list)
But in general, you'll want to do this with a recursive or iterative function if you want to do error handling beyond throwing a KeyError
.
Upvotes: 0