I have a dictionary like
{a:{b:{c:{d:2}}}, e:2, f:2}
How am I supposed to get the value of d and change it in python? Previous questions only showed how to get the level of nesting but didn't show how to get the value. In this case, I do not know the level of nesting of the dict. Any help will be appreciated, thank you!!!
P.S. I am using python 3
How about some recursion?
def unest(data, key):
if key in data.keys():
return data.get(key)
else:
for dkey in data.keys():
if isinstance(data.get(dkey), dict):
return unest(data.get(dkey), key)
else:
continue
d = {'a':{'b':{'c':{'d':25}}}, 'e':2, 'f':2}
r = unest(d, 'd')
# 25
r = unest(d, 'c')
# {'d': 25}
Edit: As Paul Rooney points out we don't have to use data.keys, we can simply iterate over the keys by doing if key in data
Edit 2: Given the input you provided this would find the deepest level, however this does not cover certain cases, such as where for example, key e is also a nested dictionary that goes n + 1 levels where n is equal to the depth of key a.
def find_deepest(data):
if not any([isinstance(data.get(k), dict) for k in data]):
return data
else:
for dkey in data:
if isinstance(data.get(dkey), dict):
return find_deepest(data.get(dkey))
else:
continue
u = find_deepest(d)
# {'d': 2}
I was searching for a similar solution, but I wanted to find the deepest instance of any key within a (possibly) nested dictionary and return it's value. This assumes you know what key you want to search for ahead of time. It's unclear to me if that is a valid assumption for the original question, but I hope this will help others if they stumble on this thread.
def find_deepest_item(obj, key, deepest_item = None):
if key in obj:
deepest_item = obj[key]
for k, v in obj.items():
if isinstance(v,dict):
item = find_deepest_item(v, key, deepest_item)
if item is not None:
deepest_item = item
return deepest_item
In this example, the 'd' key exists at the top level as well:
d = {'a':{'b':{'c':{'d':25}}}, 'd':3, 'e':2, 'f':2}
v = find_deepest_item(d, 'd')
# 25
Search for 'c':
v = find_deepest_item(d, 'c')
# {'d': 25}
Related
I want to implement a function that:
Given a dictionary and an iterable of keys,
deletes the value accessed by iterating over those keys.
Originally I had tried
def delete_dictionary_value(dict, keys):
inner_value = dict
for key in keys:
inner_value = inner_value[key]
del inner_value
return dict
Thinking that since inner_value is assigned to dict by reference, we can mutate dict implcitly by mutating inner_value. However, it seems that assigning inner_value itself creates a new reference (sys.getrefcount(dict[key]) is incremented by assigning inner_value inside the loop) - the result being that the local variable assignment is deled but dict is returned unchanged.
Using inner_value = None has the same effect - presumably because this merely reassigns inner_value.
Other people have posted looking for answers to questions like:
how do I ensure that my dictionary includes no values at the key x - which might be a question about recursion for nested dictionaries, or
how do I iterate over values at a given key (different flavours of this question)
how do I access the value of the key as opposed to the keyed value in a dictionary
This is none of the above - I want to remove a specific key,value pair in a dictionary that may be nested arbitrarily deeply - but I always know the path to the key,value pair I want to delete.
The solution I have hacked together so far is:
def delete_dictionary_value(dict, keys):
base_str = f"del dict"
property_access_str = ''.join([f"['{i}']" for i in keys])
return exec(base_str + property_access_str)
Which doesn't feel right.
This also seems like pretty basic functionality - but I've not found an obvious solution. Most likely I am missing something (most likely something blindingly obvious) - please help me see.
If error checking is not required at all, you just need to iterate to the penultimate key and then delete the value from there:
def del_by_path(d, keys):
for k in keys[:-1]:
d = d[k]
return d.pop(keys[-1])
d = {'a': {'b': {'c': {'d': 'Value'}}}}
del_by_path(d, 'abcd')
# 'Value'
print(d)
# {'a': {'b': {'c': {}}}}
Just for fun, here's a more "functional-style" way to do the same thing:
from functools import reduce
def del_by_path(d, keys):
*init, last = keys
return reduce(dict.get, init, d).pop(last)
Don't use a string-evaluation approach. Try to iteratively move to the last dictionary and delete the key-value pair from it. Here a possibility:
def delete_key(d, value_path):
# move to most internal dictionary
for kp in value_path[:-1]:
if kp in dd and isinstance(d[kp], dict):
d = d[kp]
else:
e_msg = f"Key-value delete-operation failed at key '{kp}'"
raise Exception(e_msg)
# last entry check
lst_kp = value_path[-1]
if lst_kp not in d:
e_msg = f"Key-value delete-operation failed at key '{lst_kp}'"
raise Exception(e_msg)
# delete key-value of most internal dictionary
print(f'Value "{d[lst_kp]}" at position "{value_path}" deleted')
del d[lst_kp]
d = {1: 2, 2:{3: "a"}, 4: {5: 6, 6:{8:9}}}
delete_key(d, [44, 6, 0])
#Value "9" at position "[4, 6, 8]" deleted
#{1: 2, 2: {3: 'a'}, 4: {5: 6, 6: {}}}
I am quite new to python and wondering if there is an easy way to find a value for a specific key for dictionaries within dictionaries. I am sure you could write a loop etc but wondering if there is a more direct way especially if there are multiple layers and you don't know upfront where exactly the value sits?
Let's say if I like to find the value for 'Mother'
a = {'family 1':{'Father':'Joe', 'Mother': 'Eva'}}
Thanks a lot.
def recursive_lookup(d, key):
if key in d:
return d[key]
for v in d.values():
if not isinstance(v, dict):
continue
x = recursive_lookup(v, key)
if x is not None:
return x
return None
This can be used as follows:
>>> d = {'family 1': {'Father': 'Joe', 'Mother': 'Eva'}}
>>> recursive_lookup(d, "Mother")
'Eva'
Maybe the dict is not intended to be used in this way, but I need to add more than one value to the same key. My intension is to use a kind of transitory property. If my dict is A:B and B:C, than I want to have the dict A:[B,C].
Let's make an example in order to explain better what I'd like to do:
numDict={'60':['4869'], '4869':['629'], '13':['2']}
I want it to return:
{'60':['4869','629'], '13':['2']}
For just two elements, it is possible to use something like this:
result={}
for key in numDict.keys():
if [key] in numDict.values():
result[list(numDict.keys())[list(numDict.values()).index([key])]]=[key]+numDict[key]
But what about if I have more elements? For example:
numDict={'60':['4869'], '4869':['629'], '13':['2'], '629':['427'}
What can I do in order to get returned {'60':[4869,629,427'], '13':['2']}?
def unchain(d):
#assemble a collection of keys that are not also values. These will be the keys of the final dict.
top_level_keys = set(d.keys()) - set(d.values())
result = {}
for k in top_level_keys:
chain = []
#follow the reference chain as far as necessary.
value = d[k]
while True:
if value in chain: raise Exception("Referential loop detected: {} encountered twice".format(value))
chain.append(value)
if value not in d: break
value = d[value]
result[k] = chain
return result
numDict={'60':'4869', '4869':'629', '13':'2', '629':'427'}
print(unchain(numDict))
Result:
{'60': ['4869', '629', '427'], '13': ['2']}
You might notice that I changed the layout of numDict since it's easier to process if the values aren't one-element lists. But if you're dead set on keeping it that way, you can just add d = {k:v[0] for k,v in d.items()} to the top of unchain, to convert from one to the other.
You can build your own structure, consisting of a reverse mapping of (values, key), and a dictionary of (key, [values]). Adding a key, value pair consists of following a chain of existing entries via the reverse mapping, until it finds the correct location; in case it does not exist, it introduces a new key entry:
class Groupir:
def __init__(self):
self.mapping = {}
self.reverse_mapping = {}
def add_key_value(self, k, v):
self.reverse_mapping[v] = k
val = v
key = k
while True:
try:
self.reverse_mapping[val]
key = val
val = self.reverse_mapping[val]
except KeyError:
try:
self.mapping[val].append(v)
except KeyError:
self.mapping[val] = [v]
break
with this test client:
groupir = Groupir()
groupir.add_key_value(60, 4869)
print(groupir.mapping)
groupir.add_key_value(4869, 629)
print(groupir.mapping)
groupir.add_key_value(13, 2)
print(groupir.mapping)
groupir.add_key_value(629, 427)
print(groupir.mapping)
outputs:
{60: [4869]}
{60: [4869, 629]}
{60: [4869, 629], 13: [2]}
{60: [4869, 629, 427], 13: [2]}
Restrictions:
Cycles as mentioned in comments.
Non unique keys
Non unique values
Probably some corner cases to take care of.
I have written a code for it. See if it helps.
What I have done is to go on diving in till i can go (hope you understand this statement) and mark them as visited as they will no longer be required. At the end I filter out the root keys.
numDict={'60':['4869'], '4869':['629'], '13':['2'], '629':['427']}
l = list(numDict) # list of keys
l1 = {i:-1 for i in numDict} # to track visited keys (initialized to -1 initially)
for i in numDict:
# if key is root and diving in is possible
if l1[i] == -1 and numDict[i][0] in l:
t = numDict[i][0]
while(t in l): # dive deeper and deeper
numDict[i].extend(numDict[t]) # update the value of key
l1[t] = 1 # mark as visited
t = numDict[t][0]
# filter the root keys
answer = {i:numDict[i] for i in numDict if l1[i] == -1}
print(answer)
Output:
{'60': ['4869', '629', '427'], '13': ['2']}
What would be the easiest way to go about turning this dictionary:
{'item':{'w':{'c':1, 'd':2}, 'x':120, 'y':240, 'z':{'a':100, 'b':200}}}
into this one:
{'item':{'y':240, 'z':{'b':200}}}
given only that you need the vars y and b while maintaining the structure of the dictionary? The size or number of items or the depth of the dictionary should not matter, as the one I'm working with can be anywhere from 2 to 5 levels deep.
EDIT: I apologize for the type earlier, and to clarify, I am given an array of strings (eg ['y', 'b']) which I need to find in the dictionary and then keep ONLY 'y' and 'b' as well as any other keys in order to maintain the structure of the original dictionary, in this case, it would be 'z'
A better example can be found here where I need Chipset Model, VRAM, and Resolution.
In regards to the comment, the input would be the above link as the starting dictionary along with an array of ['chipset model', 'vram', 'resolution'] as the keep list. It should return this:
{'Graphics/Displays':{'NVIDIA GeForce 7300 GT':{'Chipset Model':'NVIDIA GeForce 7300 GT', 'Displays':{'Resolution':'1440 x 900 # 75 Hz'}, 'VRAM (Total)':'256 Mb'}}
Assuming that the dictionary you want to assign to an element of a super-dictionary is foo, you could just do this:
my_dictionary['keys']['to']['subdict']=foo
Regarding your edit—where you need to eliminate all keys except those on a certain list—this function should do the trick:
def drop_keys(recursive_dict,keep_list):
key_list=recursive_dict.keys()
for key in key_list:
if(type(recursive_dict[key]) is dict):
drop_keys(recursive_dict[key], keep_list)
elif(key not in keep_list):
del recursive_dict[key]
Something like this?
d = {'item': {'w': {'c': 1, 'd': 2}, 'x': 120, 'y': 240, 'z': {'a': 100, 'b': 200}}}
l = ['y', 'z']
def do_dict(d, l):
return {k: v for k, v in d['item'].items() if k in l}
Here's what I arrived at for a recursive solution, which ended up being similar to what #Dan posted:
def recursive_del(d,keep):
for k in d.copy():
if type(d[k]) == dict:
recursive_del(d[k],keep)
if len(d[k]) == 0: #all keys were deleted, clean up empty dict
del d[k]
elif k not in keep:
del d[k]
demo:
>>> keepset = {'y','b'}
>>> a = {'item':{'w':{'c':1, 'd':2}, 'x':120, 'y':240, 'z':{'a':100, 'b':200}}}
>>> recursive_del(a,keepset)
>>> a
{'item': {'z': {'b': 200}, 'y': 240}}
The only thing I think he missed is that you will need to sometimes need to clean up dicts which had all their keys deleted; i.e. without that adjustment you would end up with a vestigial 'w':{} in your example output.
Using your second example I made something like this, it's not exactly pretty but it should be easy to extend. If your tree starts to get big, you can define some sets of rules to parse the dict.
Each rule here are actually pretty much "what should I do when i'm in which state".
def rule2(key, value):
if key == 'VRAM (Total)':
return (key, value)
elif key == 'Chipset Model':
return (key, value)
def rule1(key, value):
if key == "Graphics/Displays":
if isinstance(value, dict):
return (key, recursive_checker(value, rule1))
else:
return (key, value)
else:
return (key, recursive_checker(value, rule2))
def recursive_checker(dat, rule):
def inner(item):
key = item[0]
value = item[1]
return rule(key, value)
return dict(filter(lambda x: x!= None, map(inner, dat.items())))
# Important bits
print recursive_checker(data, rule1)
In your case, as there is not many states, it isn't worth doing it but in case you have multiple cards and you don't necessarly know which key should be traversed but only know that you want certain keys from the tree. This method could be used to search the tree easily. It can be applied to many things.
I have a lot of nested dictionaries, I am trying to find a certain key nested inside somewhere.
e.g. this key is called "fruit". How do I find the value of this key?
#Håvard's recursive solution is probably going to be OK... unless the level of nesting is too high, and then you get a RuntimeError: maximum recursion depth exceeded. To remedy that, you can use the usual technique for recursion removal: keep your own stack of items to examine (as a list that's under your control). I.e.:
def find_key_nonrecursive(adict, key):
stack = [adict]
while stack:
d = stack.pop()
if key in d:
return d[key]
for k, v in d.iteritems():
if isinstance(v, dict):
stack.append(v)
The logic here is quite close to the recursive answer (except for checking for dict in the right way;-), with the obvious exception that the recursive calls are replaced with a while loop and .pop and .append operations on the explicit-stack list, stack.
(Making some wild guesses about your data structure...)
Do it recursively:
def findkey(d, key):
if key in d: return d[key]
for k,subdict in d.iteritems():
val = findkey(subdict, key)
if val: return val
Just traverse the dictionary and check for the keys (note the comment in the bottom about the "not found" value).
def find_key_recursive(d, key):
if key in d:
return d[key]
for k, v in d.iteritems():
if type(v) is dict: # Only recurse if we hit a dict value
value = find_key_recursive(v, key)
if value:
return value
# You may want to return something else than the implicit None here (and change the tests above) if None is an expected value
Almost 11 years later... based on Alex Martelli answer with slight modification, for Python 3 and lists:
def find_key_nonrecursive(adict, key):
stack = [adict]
while stack:
d = stack.pop()
if key in d:
return d[key]
for v in d.values():
if isinstance(v, dict):
stack.append(v)
if isinstance(v, list):
stack += v
I have written a handy library for this purpose.
I am iterating over ast of the dict and trying to check if a particular key is present or not.
Do check this out. https://github.com/Agent-Hellboy/trace-dkey
An example from README
>>> from trace_dkey import trace
>>> l={'a':{'b':{'c':{'d':{'e':{'f':1}}}}}}
>>> print(trace(l,'f'))
[['a', 'b', 'c', 'd', 'e', 'f']]
Now you can query it as l['a']['b']['c']['d']['e']['f']
>>> l['a']['b']['c']['d']['e']['f']
1