Search for key in nested Python dictonary - python

I want to search for key-value pairs in Python dict. My dict is nested, so simple solution like:
if 'keyname' in my_dict:
...
or:
for key, value in my_dict.items():
if 'keyname' == key:
does not work for me.
This is my dict:
my_dict = {'a': 111,
'b': 222,
'c':{'d': 333,
'e': 444},
'f':555,
'g':{'h': {'i': 666}}}
And I want to find value of 'i' or 'd', whats the simplest way to do that?
I do not care for name of the key that has nested dict. For example, I do not care for key name of 'c' or 'g' or 'h', I only care about key-values (key-number).
Is there a way to transform my dict to look like this:
my_result = {'a': 111,
'b': 222,
'd': 333,
'e': 444,
'f': 555,
'i': 666}
In that way it would be easy.

You can flatten the dictionary using recursion here.
def flat(data):
out = {}
for k, v in data.items():
if isinstance(v, dict):
out.update(flat(v))
else:
out[k] = v
return out
flat(my_dict)
# {'a': 111, 'b': 222, 'd': 333, 'e': 444, 'f': 555, 'i': 666}

try this one:
def find_value(_key, _dict):
for key, value in list(_dict.items()):
if isinstance(value, dict):
result = find_value(_key, value)
if result:
return result
if key == _key:
return value

Related

How can I transform a value from a dictionary to a key and build a nested dictionary with the subsequent values

I'm new to dict structures and I have the following:
d = {'a': 'aa', 'b': 'bb', 'c': 'cc', 'x':111, 'y': 222, 'z':333}
And I want to change to the following structure:
d2 = {'a': 'aa', 'b': 'bb', 'cc':{ 'x':111, 'y': 222, 'z':333}}
Basically, the value of a given key will become the key itself and subsequent values will go into this new key as a nested dict.The values of x, y and z are demonstrative only, they can be floats or integers. Always after the c key, will come the values that need to be placed inside c. Thank you
Here is a little longer solution fitted for more general cases:
d = {'a': 'aa', 'b': 'bb', 'c': 'cc', 'x':111, 'y': 222, 'z':333}
slicing_key = 'c'
svalue = d[slicing_key]
d2 = {}
d2[svalue] = {}
# List of all keys
all_keys = list(d.keys())
# Index of the key where you slice
slice_ind = all_keys.index(slicing_key)
# Unchanged entries
orig_keys = all_keys[:slice_ind]
# Keys for entries in the inner dict
new_keys = all_keys[slice_ind+1:]
# Assemble the new dictionary
for key in orig_keys:
d2[key] = d[key]
for key in new_keys:
d2[svalue][key] = d[key]
First you pre-define your output dictionary, then you split up the list of all keys based on your slicing key criterion, and finally you assemble the new dictionary.
Note that this relies on the fact that the list returned by keys() is ordered. It should be this way now but you can also sort it to be on the safe side, all_keys = list(sorted(d.keys())).
A tricky solution would be to use pandas.DataFrame constructor from pandas :
import pandas as pd
def nest_dict(d, val):
v = next((k for k, v in d.items() if v == val), None)
df = pd.DataFrame(d, index=[0])
idx = df.columns.get_loc(v)
out = df.iloc[:, :idx].to_dict("records")[0]
out[val] = df.iloc[:, idx+1:].to_dict("records")[0]
return out
d2 = nest_dict(d1, "cc")
Output :
print(d2)
#{'a': 'aa', 'b': 'bb', 'cc': {'x': 111, 'y': 222, 'z': 333}}
This seems like a vague question and more details regarding the constraints/rules should be provided. However, with the given details, here is a piece of code that should help you out.
import itertools
def slice_dic(dic, k):
keys = list(dic)
lts = keys.index(k)
d = dict(itertools.islice(dic.items(), lts))
d2 = dict(itertools.islice(dic.items(), lts+1,len(keys)))
d[list(dic.values())[lts]] = d2
return d
input_dict = {'a': 'aa', 'b': 'bb', 'c': 'cc', 'x':111, 'y': 222, 'z':333}
slice_dic(input_dict, "c") #slices the dictionary at c
#and keeps all subsequent key:value pairs as a dictinoary
The above snippet gives the following output:
{'a': 'aa', 'b': 'bb', 'cc': {'x': 111, 'y': 222, 'z': 333}}
With limited information present and without using any libraries
def spl_dict(d, pos):
d2 = dict(list(d.items())[:pos-1])
d2[list(d.values())[pos-1]]= dict(list(d.items())[pos:])
return d2
print(spl_dict(d,3))
Output :
{'a': 'aa', 'b': 'bb', 'cc': {'x': 111, 'y': 222, 'z': 333}}
Assuming that you hold an insertion order of your input dict, the solution with itertools.groupby util to get sequential groups counting on c key:
from itertools import groupby
d = {'a': 'aa', 'b': 'bb', 'c': 'cc', 'x':111, 'y': 222, 'z':333}
head, c, tail = [list(gr) for _, gr in groupby(d.items(), lambda t: t[0] == 'c')]
c_k, c_v = c[0] # for c key
res = {**dict(head), **{c_v: dict(tail)}}
{'a': 'aa', 'b': 'bb', 'cc': {'x': 111, 'y': 222, 'z': 333}}

How to convert string to dict and if same key appears twice then make its value as list

s = 'o=foo t=bar t=baz f=zap'
How to convert this string into dict, but if same key is repeated, make it as list?
Expecting:
{'o': 'foo', 't': ['bar','baz'], 'f': 'zap'}
Tried:
>>> dict(token.split('=') for token in shlex.split(s))
{'o': 'foo', 't': 'baz', 'f': 'zap'}
You can define a function and pass a generator:
s = 'o=foo t=bar t=baz f=zap'
q = 'o=foo t=bar t=baz t=bop f=zap'
def dict_fromitems(items):
d = dict()
for (k, v) in items:
if k in d:
d[k] = (d[k] if type(d[k])==list else [d[k]]) + [v]
else:
d[k] = v
return d
s_dict = dict_fromitems((i.split('=') for i in s.split()))
q_dict = dict_fromitems((i.split('=') for i in q.split()))
Returns:
{'o': 'foo', 't': ['bar', 'baz'], 'f': 'zap'}
{'o': 'foo', 't': ['bar', 'baz', 'bop'], 'f': 'zap'}
Iterate over the potential key value pair generated by splitting on = and check if the key is already present and check if the value is instance of list and if it is list append the value to it else create the list with the existing string value and append the value to it. If key is not present just create a new key with a value.
s = 'o=foo t=bar t=baz f=zap'
s_list=s.split()
d={}
for i in s_list:
k,v=i.split('=')
if k in d:
if isinstance(d[k],list):
d[k].append(v)
else:
d[k]=[d[k]]
d[k].append(v)
else:
d[k]=v
print(d)
output:
{'f': 'zap', 'o': 'foo', 't': ['bar', 'baz']}

Weave two dictionaries into one

I have two dictionaries:
dict1 = {'a': 1,
'b': 2,
'c': 3,
'd': 4,
'x': 5}
and
dict2 = {'a': 'start',
'b': 'start',
'c': 'end',
'd': 'end'}
I am trying to create a new dictionary that maps the values start and end as keys to a dictionary that would contain the info of dict1, while keeping those that are not present in dict2 as keys, e.g.:
dict3 = {'start': {'a': 1, 'b': 2},
'end': {'c': 3, 'd': 4},
'x': {'x': 5}
}
Use dict.setdefault() to create the nested dictionaries in dict3 if not yet there, and dict.get() to determine the key in the top-level output dictionary:
dict3 = {}
for k, v in dict1.items():
nested = dict3.setdefault(dict2.get(k, k), {})
nested[k] = v
So dict2.get(k, k) will produce the value from dict2 for a given key from dict1, using the key itself as a default. So for the 'x' key, that'll produce 'x' as there is no mapping in dict2 for that key.
Demo:
>>> dict3 = {}
>>> for k, v in dict1.items():
... nested = dict3.setdefault(dict2.get(k, k), {})
... nested[k] = v
...
>>> dict3
{'start': {'a': 1, 'b': 2}, 'end': {'c': 3, 'd': 4}, 'x': {'x': 5}}
I actually figured it out while abstracting the example and typing up my question here (should have maybe done this earlier...). Anyways: Yay!
So here is my solution, in case it may help someone. If someone knows a swifter or more elegant way to do it, I would be glad to learn!
dict3 = dict()
for k, v in dict1.items():
# if the key of dict1 exists also in dict2
if k in dict2.keys():
# get its value (the keys-to-be for the new dict3)
new_key = dict2[k]
# if the new key is already in the new dict
if new_key in dict3.keys():
# appends new dict entry to dict3
dict3[new_key].update({k: v})
# otherwise create a new entry
else:
dict3[new_key] = {k: v}
# if there is no corresponding mapping present
else:
# treat the original key as the new key and add to dict3
no_map = k
dict3[no_map] = {k: v}

Merge values of dictionary into one if keys match condition

I have a dictionary called, say, tempDict which is filled as such:
tempDict = {'a': 100, 'b': 200, 'c': 120, 'b_ext1': 4, 'd': 1021, 'a_ext1': 21, 'f_ext1': 12}
In my setup, I need to loop over the keys and if for any key that has a '_ext1' suffix, I want to rewrite or create a new dictionary keeping an unaltered key (preferably without the 'ext1') but with the values merged.
i.e.:
newDict = {'a': 121, 'b': 204, 'c': 120, 'd': 1021, 'f_ext1':12}
Notice, the last entry in the dictionary should be unchanged as there is no 'f' unsuffixed with '_ext1'
The values themselves won't be integers, however the operation is similar.
Does anyone have any ideas?
newDict = {}
for k in tempDict:
if k.endswith("_ext1") and k[:-5] in tempDict:
newDict[k[:-5]] = newDict.get(k[:-5],0)+tempDict[k]
else:
newDict[k] = newDict.get(k,0)+tempDict[k]
Iterate over the keys (items) in the sorted order and sum or add values. This works because keys like "a", "f" sort before "a_ext1", "f_ext1":
>>> d = {}
>>> for k, v in sorted(tempDict.items()):
... if k[0] in d:
... d[k[0]] += v
... else:
... d[k] = v
...
>>> d
{'a': 121, 'c': 120, 'b': 204, 'd': 1021, 'f_ext1': 12}

How to reverse a dictionary that has repeated values

I have a dictionary with almost 100,000 (key, value) pairs and the majority of the keys map to the same values. For example:
mydict = {'a': 1, 'c': 2, 'b': 1, 'e': 2, 'd': 3, 'h': 1, 'j': 3}
What I want to do, is to reverse the dictionary so that each value in mydict is going to be a key at the reverse_dict and is going to map to a list of all the mydict.keys() that used to map to that value in mydict. So based on the example above I would get:
reversed_dict = {1: ['a', 'b', 'h'], 2: ['c', 'e'] , 3: ['d', 'j']}
I came up with a solution that is very expensive and I want to hear any ideas for doing this more efficiently than this:
reversed_dict = {}
for value in mydict.values():
reversed_dict[value] = []
for key in mydict.keys():
if mydict[key] == value:
if key not in reversed_dict[value]:
reversed_dict[value].append(key)
Using collections.defaultdict:
from collections import defaultdict
reversed_dict = defaultdict(list)
for key, value in mydict.items():
reversed_dict[value].append(key)
reversed_dict = {}
for key, value in mydict.items():
reversed_dict.setdefault(value, [])
reversed_dict[value].append(key)
for k,v in dict.iteritems():
try:
reversed_dict[v].append(k)
except KeyError:
reversed_dict[v]=[k]
I think you're wasting a few cycles by replacing a key with the same key again and again...
reversed_dict = {}
for value in mydict.values():
if value not in reversed_dict.keys(): #checking to be sure it hasn't been done.
reversed_dict[value] = []
for key in mydict.keys():
if mydict[key] == value:
if key not in reversed_dict[value]: reversed_dict[value].append(key)
Using itertools.groupby:
from operator import itemgetter
from itertools import groupby
snd = itemgetter(1)
def sort_and_group(itr, f):
return groupby(sorted(itr, key=f), f)
mydict = {'a': 1, 'c': 2, 'b': 1, 'e': 2, 'd': 3, 'h': 1, 'j': 3}
reversed_dict = {number: [char for char,_ in v]
for number, v in sort_and_group(mydict.items(), snd)}
reversed_dict = collections.defaultdict(list)
for key, value in dict_.iteritems():
reversed_dict[value].append(key)
def reverse_dict(mydict):
v={}
for x,y in mydict.items():
if y not in v:
v[y]=[x]
else:
v[y].append(x)
return v
print(reverse_dict(mydict))

Categories