Let's say I have a dictionary called my_dic:
my_dict = {'a': {'spam': {'foo': None, 'bar': None, 'baz': None},'eggs': None}, 'b': {'ham': None}}
Then if I input spam, it should return a, and if I input bar it should return spam. If I input b, it should return None. Basically getting the parent of the dictionary.
How would I go about doing this?
A simple recursive function, which returns the current key if needle in v is true; needle in v simply testing if the key exists in the associated value:
my_dict = {'a': {'spam': {'foo': None, 'bar': None, 'baz': None},'eggs': None}, 'b': {'ham': None}}
def get_parent_key(d: dict, needle: str):
for k, v in d.items():
if isinstance(v, dict):
if needle in v:
return k
if found := get_parent_key(v, needle):
return found
print(get_parent_key(my_dict, 'bar'))
Related
So I have this params:
p = [{'quantity': 1}, {'args': {'id': 12345678, 'age': 12}}]
And I want to be able to search for quantity and get the value 1 or the args key and get its doctionary ({'id': 12345678, 'age: 12})
This is what I have try:
def search(name: str, params: list[dict]):
try:
return next((x[name] for x in params if x), None)
except KeyError:
return None
I case I search for the value of quantity:
search(name='quantity', params=p)
This return 1
But in case I want to value of args:
search(name='args', params=p)
This return None
I have a set of functions that you could use for this [ getNestedVal(p, 'args') would return {'id': 12345678, 'age: 12} ]...or you can use this generator
def yieldNestedVals(obj, key):
if isinstance(obj, str): return
try: yield obj[key]
except: pass
if isinstance(obj, dict): obj = obj.values()
for i in (obj if hasattr(obj, '__iter__') else []):
for v in yieldNestedVals(i, key): yield v
inside search like
def search(name, params, defaultVal=None):
for v in yieldNestedVals(params, name): return v # return 1st generated value
return defaultVal # if nothing if generated
[ Ofc you can omit both the defaultVal parameter and the last line if you want to just return None when nothing if found, since that is the default behavior when a function doesn't come across a return statement. ]
Now search('args', p) should also return {'id': 12345678, 'age: 12} and you can try it with other keys as well:
# p = [{'quantity': 1}, {'args': {'id': 12345678, 'age': 12}}]
{k: search(k, p) for k in ['quantity', 'args', 'id', 'age', 'dneKey']}
would return
{'quantity': 1,
'args': {'id': 12345678, 'age': 12},
'id': 12345678,
'age': 12,
'dneKey': None}
Say we have a json dict our_dict = {'foo': { 'key': 'value' }, 'fizz': 'buzz'}
Find function f(d, l_keys, value) that changes the value in d indexed by the list of keys l_keys and returns d. The length of the list could be varying, error handling is not required.
Example:
f(out_dict, ['foo', 'key'], 'a new value')
should return
{'foo': { 'key': 'a new value' }, 'fizz': 'buzz'}
My attempt so far:
_tmp = d[l_keys[0]]
for i in range(1, len(l_keys)):
_tmp = _tmp[l_keys[i]]
_tmp = value
The problem is that changes to _tmp are not reflected in d
JSON is a string format, there is no such JSON dict, becaues dict is a python structure. our_dict = {'foo': { 'key': 'value' }, 'fizz': 'buzz'} is only a python dict
I'd suggest you iterate on the param keys except the last, to read the interesting inner dict, then do the replacement
def f(our_dict: dict, param: list[str], param1: str):
tmp_dict = our_dict
for p in param[:-1]:
tmp_dict = tmp_dict[p]
tmp_dict[param[-1]] = param1
return our_dict
Examples
our_dict = {'foo': {'key': 'value'}, 'fizz': 'buzz'}
print(f(our_dict, ['foo', 'key'], 'a new value'))
# {'foo': {'key': 'a new value'}, 'fizz': 'buzz'}
our_dict = {'foo': {'key': {'a': {'b': 'value'}}}, 'fizz': 'buzz'}
print(f(our_dict, ['foo', 'key'], 'a new value'))
# {'foo': {'key': 'a new value'}, 'fizz': 'buzz'}
our_dict = {'foo': {'key': {'a': {'b': 'value'}}}, 'fizz': 'buzz'}
print(f(our_dict, ['foo', 'key', 'a', 'b'], 'a new value'))
# {'foo': {'key': {'a': {'b': 'a new value'}}}, 'fizz': 'buzz'}
Here is a nested dictionary:
fdict = {}
fdict['apple'] = {}
fdict['banana'] = {}
fdict['apple']['green'] = 5
fdict['apple']['red'] = 0
fdict['banana']['light_yellow'] = 10
fdict['banana']['dark_yellow'] = 0
fdict['appraisal round'] = 1
{'apple': {'green': 5, 'red': 0},
'banana': {'light_yellow': 10, 'dark_yellow': 0},
'appraisal round': 1}
What is the most pythonic way to remove the key and value pairs where the value is zero from this nested dictionary such that the following dictionary results:
{'apple': {'green': 5}, 'banana': {'light_yellow': 10}, 'appraisal round': 1}
Note: The appraisal round key does not have a dictionary as a value itself.
Here is what I have implemented so far:
overall_dict = {}
for key in [key for key in fdict.keys() if key != 'appraisal round']:
new_dict = {k:v for k,v in fdict[key].items() if v != 0}
overall_dict[key] = new_dict
overall_dict['appraisal round'] = 1
However, using a temporary dictionary, constructing an entirely new dictionary and adding back in the appraisal round does not seem like a clean approach. Is there perhaps a way to amend the existing dictionary more effectively?
I suggest the following solution (it works for multiple nested levels):
fdict = {
'apple': {'green': 5, 'red': 0},
'banana': {'light_yellow': 10, 'dark_yellow': 0},
'appraisal round': 1
}
noZero = lambda d: { k1: noZero(v1) if isinstance(v1, dict) else v1 for k1, v1 in d.items() if v1 }
print(noZero(fdict)) # {'apple': {'green': 5}, 'banana': {'light_yellow': 10}, 'appraisal round': 1}
Assuming that You do not know the key that has non-dict type, a solution could be:
fdict = {}
fdict['apple'] = {}
fdict['banana'] = {}
fdict['apple']['green'] = 5
fdict['apple']['red'] = 0
fdict['banana']['light_yellow'] = 10
fdict['banana']['dark_yellow'] = 0
fdict['appraisal round'] = 1
def non_zero_value(item):
k, v = item
return v != 0
result = {}
for k, v in fdict.items():
if isinstance(v, dict):
result[k] = dict(filter(non_zero_value, v.items()))
else:
result[k] = v
print(result)
# {'apple': {'green': 5}, 'banana': {'light_yellow': 10}, 'appraisal round': 1}
I've two nested dictionaries of variable depth orig_dict , new_dict
orig_dict = {"a":1, "b":[{"c":{"d":2}, "e":[{"q":12}, {"a":2}]}, {"h":[1,2,3,4]}], "e":{"we":12}}
new_dict = {"a":2, "b":[{"c":{"d":3}, "e":[{"q":120}, {"a":2}, {"x":10000}]}], "e":{"we":12}, "f":100}
Here new_dict is the updates needed to be done in orig_dict,
if key doesn't exists add that key-val to orig_dict
if key in orig_dict doesn't exists in new_dict then delete key-val from orig_dict
if key matches skip changes
I've code which will process only dictionaries as values but not list
import collections
def map_and_update(orig_dict, new_dict):
for key, val in new_dict.items():
if isinstance(val, collections.Mapping):
tmp = updatee(orig_dict.get(key, { }), val)
orig_dict[key] = tmp
else:
orig_dict[key] = new_dict[key]
return orig_dict
this will result:
{'a': 2, 'b': [{'c': {'d': 3}, 'e': [{'q': 120}, {'a': 2}, {'x': 10000}]}], 'e': {'we': 12}, 'f': 100}
but i expect
{'a': 1, 'b': [{'c': {'d': 2}, 'e': [{'q': 12}, {'a': 2}, {'x': 10000}]}], 'e': {'we': 12}, 'f': 100}
Note: Above i'm just copying the value of new_dict to orig_dict if i
see a list, but it should parse through list even and check whether
dict exists or not if exists then again do map
This code of mine is maybe not very readable, but at least it did finish the job. I dont have time right now, but if anyone want me to explain, i will edit this post later.
orig_dict = {"a":1, "b":[{"c":{"d":2}, "e":[{"q":12}, {"a":2}]}, {"h":[1,2,3,4]}], "e":{"we":12}}
new_dict = {"a":2, "b":[{"c":{"d":3}, "e":[{"q":120}, {"a":2}, {"x":10000}]}], "e":{"we":12}, "f":100}
for key, value in new_dict.items():
if isinstance(value, list):
for key1, value1 in new_dict['b'][0].items():
if orig_dict['b'][0].get(key1):
pass
if len(new_dict['b']) != len(orig_dict['b']):
del orig_dict['b'][len(new_dict['b']):]
length = len(new_dict['b'][0]['e'])
for i in range(length):
for k, v in new_dict['b'][0]['e'][i].items():
try:
if orig_dict['b'][0]['e'][i].get(k):
pass
else:
orig_dict['b'][0]['e'][i][k]=v
except:
orig_dict['b'][0]['e'].append({k:v})
elif orig_dict.get(key):
pass
else:
orig_dict[key]=value
print(orig_dict)
I have a dictionary that looks like this:
{'items': [{'id': 1}, {'id': 2}, {'id': 3}]}
and I'm looking for a way to directly get the inner dictionary with id = 1.
Is there a way to reach this other than looping the list items and comparing the id?
first_with_id_or_none = \
next((value for value in dictionary['items'] if value['id'] == 1), None)
You will have to loop through the list. The good news is is that you can use a generator expression with next() to do that looping:
yourdict = next(d for d in somedict['items'] if d['id'] == 1)
This can raise a StopIteration exception if there is no such matching dictionary.
Use
yourdict = next((d for d in somedict['items'] if d['id'] == 1), None)
to return a default instead for that edge-case (here None is used, but pick what you need).
Make it into a function:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items():
for d in v:
if d.get(key) == value:
return d
else:
raise ValueError('the dictionary was not found')
With explanation:
def get_inner_dict_with_value(D, key, value):
for k, v in D.items(): # loop the dictionary
# k = 'items'
# v = [{'id': 1}, {'id': 2}, {'id': 3}]
for d in v: # gets each inner dictionary
if d.get(key) == value: # find what you look for
return d # return it
else: # none of the inner dictionaries had what you wanted
raise ValueError('the dictionary was not found') # oh no!
Running it:
>>> get_inner_dict_with_value({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}
Another method:
def get_inner_dict_with_value2(D, key, value):
try:
return next((d for l in D.values() for d in l if d.get(key) == value))
except StopIteration:
raise ValueError('the dictionary was not found')
>>> get_inner_dict_with_value2({'items': [{'id': 1}, {'id': 2}, {'id': 3}]}, 'id', 1)
{'id': 1}