I'm trying to manipulate nested dictionaries to combine backwards the keys of any nested dictionaries with only a single key.
I've tried to do this recursively, but I'm having a hard time being able to remove keys from the dictionary and replace them with the concatenated keys.
For example:
{'adv':
{'e':
{'r':
{
's':
{'e':
{'_end_': '_end_'}
},
't':
{'_end_': '_end_',
'i':
{'s':
{'e':
{'r':
{'_end_': '_end_'}
}
}
}
}
}
},
'i': {'c': {'e': {'_end_': '_end_'}
}
}
}
}
would become
{'adv':
{'er':
{
'se':
{'_end_': '_end_'},
't':
{'_end_': '_end_',
'iser':
{'_end_': '_end_'}
}
},
'ice':
{'_end_': '_end_'}
}
}
This was an interesting problem - there is probably a more elegant solution, but I did the following:
import pprint
t={'adv': {'e': {'r': {'s': {'e': {'_end_': '_end_'}},
't': {'_end_': '_end_',
'i': {'s': {'e': {'r': {'_end_': '_end_'}}}}}}},
'i': {'c': {'e': {'_end_': '_end_'}}}}}
def concat_dict(d):
if d == '_end_':
return '_end_'
rv = {}
for k, v in d.items():
if '_end_' in v:
rv[k] = concat_dict(v)
elif len(list(x for x in v.keys() if x != '_end_')) == 1:
top_str = k
next_str = list(v.keys())[0]
rv[top_str + next_str] = concat_dict(v[next_str])
else:
rv[k] = concat_dict(v)
return rv
def format_dict(d):
while concat_dict(d) != d:
d = concat_dict(d)
return d
pprint.pprint(format_dict(t))
Output:
{'adv': {'er': {'se': {'_end_': '_end_'},
't': {'_end_': '_end_', 'iser': {'_end_': '_end_'}}},
'ice': {'_end_': '_end_'}}}
Related
I have a variable which stores below dictionary
initial_ltp =
{'s': 'ok',
'd': [{'n': 'MCX:CRUDEOIL23JANFUT',
's': 'ok',
'v': {'ch': 47.0,
'chp': 0.74,
'lp': 6377.0,
'spread': 2.0,
'ask': 6379.0,
'bid': 6377.0,
'open_price': 6330.0,
'high_price': 6393.0,
'low_price': 6305.0,
'prev_close_price': 6330.0,
'volume': 8410,
'short_name': 'CRUDEOIL23JANFUT',
'exchange': 'MCX',
'description': 'MCX:CRUDEOIL23JANFUT',
'original_name': 'MCX:CRUDEOIL23JANFUT',
'symbol': 'MCX:CRUDEOIL23JANFUT',
'fyToken': '1120230119244999',
'tt': 1673481600,
'cmd': {'t': 1673518200,
'o': 6376.0,
'h': 6378.0,
'l': 6375.0,
'c': 6377.0,
'v': 19,
'tf': '15:40'}}},
{'n': 'MCX:SILVERMIC23FEBFUT',
's': 'ok',
'v': {'ch': 485.0,
'chp': 0.71,
'lp': 68543.0,
'spread': 5.0,
'ask': 68545.0,
'bid': 68540.0,
'open_price': 68200.0,
'high_price': 68689.0,
'low_price': 68200.0,
'prev_close_price': 68058.0,
'volume': 49595,
'short_name': 'SILVERMIC23FEBFUT',
'exchange': 'MCX',
'description': 'MCX:SILVERMIC23FEBFUT',
'original_name': 'MCX:SILVERMIC23FEBFUT',
'symbol': 'MCX:SILVERMIC23FEBFUT',
'fyToken': '1120230228242738',
'tt': 1673481600,
'cmd': {'t': 1673518200,
'o': 68525.0,
'h': 68543.0,
'l': 68524.0,
'c': 68543.0,
'v': 140,
'tf': '15:40'}}}]}
I am trying to collect ('n') and ('lp') and save it in a different dictionary using code:
if 'd' in initial_ltp.keys():
ltp[initial_ltp['d'][0]['n']] = initial_ltp['d'][0]['v']['lp']
But it is only taking the first n and lp
ltp
{'MCX:CRUDEOIL23JANFUT': 6377.0}
My expected output:
ltp
{'MCX:CRUDEOIL23JANFUT': 6377.0, 'MCX:SILVERMIC23FEBFUT': 68543.0}
How can I get both the values
You have to loop over the list. Using ltp[initial_ltp['d'][0] will just extract for the first element of the list.
Here is an example:
results = {}
for doc in initial_ltp["d"]:
results[doc["n"]] = doc["v"]["lp"]
print(results)
Use the following approach with dict comprehension:
ltp = {d['n']:d['v']['lp'] for d in initial_ltp['d']} if 'd' in initial_ltp else {}
{'MCX:CRUDEOIL23JANFUT': 6377.0, 'MCX:SILVERMIC23FEBFUT': 68543.0}
when you use the "=" operator, it replaces your value in the dictionary key.
you want to add keys to your dictionary so I suggest using this:
if 'd' in initial_ltp.keys():
for o in initial_ltp['d']:
if n in o:
ltp[initial_ltp['d'][0]['n']].append(initial_ltp['d'][0]['v']
['lp']
It's because you selected only first item in the 'd'.
try a loop like this:
ltp={}
if 'd' in initial_ltp.keys():
for i in range(len(initial_ltp['d'])):
ltp[initial_ltp['d'][i]['n']] = initial_ltp['d'][i]['v']['lp']
print (ltp)
Output:
{'MCX:CRUDEOIL23JANFUT': 6377.0, 'MCX:SILVERMIC23FEBFUT': 68543.0}
I have a dictionary that looks like:
my_dict = {
'A': 'update_me',
'B': {
'C': 'D',
'E': 'F'
},
'G': {
'H': 'update_me',
'I': 'J',
'K': 'update_me'
}
}
I'm trying to create a function that will loop through every key value pair and determine if that value is update_me. If it is, it will set that value equal to this_worked. So it'd look like this:
my_dict = {
'A': 'this_worked',
'B': {
'C': 'D',
'E': 'F'
},
'G': {
'H': 'this_worked',
'I': 'J',
'K': 'this_worked'
}
}
In addition to this, I would like this to be dynamic, so that the code doesn't have to explicitly look for my_dict['A'] or my_dict['G']['H']. It should just loop through each key value pair, and if that value is update_me, then update it (I have other dictionaries that I need to update in a similar way, but their keys, lengths and depths are varying).
I think I really just need a way to loop through every level of a dictionary that has any number of particular levels.
An easy way to handle operations with arbitrary levels of nesting is a recursive function. In this case, you want to perform an operation on each item in a dictionary, and do that same thing for each item that is itself a dictionary:
>>> def recursive_replace(d, old, new):
... if d == old:
... return new
... if not isinstance(d, dict):
... return d
... return {k: recursive_replace(v, old, new) for k, v in d.items()}
...
>>> recursive_replace(my_dict, "update_me", "this_worked")
{'A': 'this_worked', 'B': {'C': 'D', 'E': 'F'}, 'G': {'H': 'this_worked', 'I': 'J', 'K': 'this_worked'}}
A solution could be:
def replace(my_dict, old_test="update_me", new_text="this_worked"):
for x, y in my_dict.items():
if type(y) is dict:
replace(y)
elif type(y) is str:
if y == old_text:
y = new_text
my_dict[x] = y
return my_dict
You can achieve this by this
my_dict = {
'A': 'update_me',
'B': {
'C': 'D',
'E': 'F'
},
'G': {
'H': 'update_me',
'I': 'J',
'K': 'update_me'
}
}
old_value = "update_me"
new_value = "new_value"
def replace_value(my_dict, old_value, new_value):
for key, value in my_dict.items():
if type(value) is dict:
replace_value(value, old_value, new_value)
elif value == old_value:
my_dict[key] = new_value
return my_dict
my_dict = replace_value(my_dict, old_value, new_value)
print(my_dict)
# {'A': 'new_value', 'B': {'C': 'D', 'E': 'F'}, 'G': {'H': 'new_value', 'I': 'J', 'K': 'new_value'}}
I have a list of dicts. Each dict can be nested. I want to remove the key id from each one of the dics, recursively. Fopr example (Note that I don't know if the amount of levels):
"files" : [
{
'id': 'ada21321',
'd': 'asdasdas',
'data': {
'd': 'asdasdas'
}
},
{
'id': 'ada23112341321',
'd': 'asdasdas',
'data': {
'd': 'asdasdas',
'id': 'asdasd21asda'
}
}
],
I don't know how nested the dics are, and where id is located. I need to remove id from all of the dics from all levels. Output:
"files" : [
{
'd': 'asdasdas',
'data': {
'd': 'asdasdas'
}
},
{
'd': 'asdasdas',
'data': {
'd': 'asdasdas'
}
}
],
I know how to remove in one level:
for current_file in data["files"]:
current_file.pop('id', None)
Is there an elegant way to achieve it?
This should do it for you:
def remove_key(container, key):
if type(container) is dict:
if key in container:
del container[key]
for v in container.values():
remove_key(v, key)
if type(container) is list:
for v in container:
remove_key(v, key)
remove_key(data['files'], 'id')
Output:
{'files': [{'d': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'d': 'asdasdas', 'data': {'d': 'asdasdas'}}]}
You can use recursion:
data = {'files': [{'id': 'ada21321', 'd': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'id': 'ada23112341321', 'd': 'asdasdas', 'data': {'d': 'asdasdas', 'id': 'asdasd21asda'}}]}
def d_rem(d):
if not isinstance(d, dict):
return d if not isinstance(d, list) else list(map(d_rem, d))
return {a:d_rem(b) for a, b in d.items() if a != 'id'}
new_d = d_rem(data)
Output:
{'files': [{'d': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'d': 'asdasdas', 'data': {'d': 'asdasdas'}}]}
This should do the trick (note that this will remove any id keys regardless of whether the associated value to that id key is a str or dict):
def remove_id(file):
for k in list(file.keys()):
if isinstance(file[k], dict):
remove_id(file[k])
if k=='id':
del file[k]
for file in files:
remove_id(file)
Yields:
[{'d': 'asdasdas', 'data': {'d': 'asdasdas'}}, {'d': 'asdasdas', 'data': {'d': 'asdasdas'}}]
I have a function which results
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}]
I have another function i need to pass 2132,L,Y values from dict1 as an arguments and it should result 452.2
def getx(a, b, c):
try:
return dict1[a][b][c]
except:
return None
when i give dict1['2132'] which results [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}]
i want dict1['2132']['L']['Y'] should result as 452.2
so i need my dictionary
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}]
to show as
dict1 = {'2132': {{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}}, '2345': {{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}}
OR is there any other way that we can pull the 4th value when the dict1 is
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'L': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}}, {'6': {'Y': '45.23'}}]
here is your solution
#v1='2132' v2='L' v3='Y'
def Solution(v1,v2,v3):
if v1 in dict1.keys():
for i in dict1[v1]:
if v2 in i.keys():
if v3 in i[v2]:
return i[v2][v3]
return None
dict1 = {'2132': [{'L': {'Y': '452.2'}}, {'C': {'N': '21'}}], '2345': [{'L': {'Y': '87'}}, {'C': {'N': '56'}},{'6': {'Y': '45.23'}}]}
print(Solution('2132','L','Y'))
How about this:
from collections import defaultdict
for key,values in dict1.items():
temp_dict = defaultdict(dict)
for val in values: #values is a list of dict
for k,v in val.items():
temp_dict[k].update(v)
dict1[key] = dict(temp_dict)
print(dict1)
#{'2132': {'L': {'Y': '452.2', 'N': '21'}}, '2345': {'L': {'Y': '87'}, 'C': {'N': '56'}, '6': {'Y': '45.23'}}}
And then
def getx(a, b, c):
try:
return dict1[a][b][c]
except:
return None
print(getx('2132','L','Y'))
#452.2
Given a nested dictionary:
nested = {
'A': {
'B': {
'C': 'C val',
'G': 'G val'
},
'D': {
'E': {
'F': 'F val'
}
}
}
}
I want to recursively concatenate the keys of the dictionary, except for the "final" key-value pairs, and put the concatenated keys in a new dictionary, like so:
expected = {
'A:B': {'C': 'C val', 'G': 'G val'},
'A:D:E': {'F': 'F val'}
}
How can I make such a function, without knowing the structure of the nested dict beforehand?
A recursive solution is the simplest. This code does as you ask.
def flatten(dictionary, prefix=[], result={}):
for k, v in dictionary.iteritems():
type_v = type(v)
if type_v == dict:
flatten(v, prefix+[k], result)
elif type_v == str:
prefix_str = ':'.join(prefix)
if not prefix_str in result:
result[prefix_str] = {}
result[prefix_str][k] = v
else:
raise TypeError('%s not permissible in data structure' % type_v)
return result
nested = {
'A': {
'B': {
'C': 'C val',
'G': 'G val',
},
'D': {
'E': {
'F': 'F val',
}
}
}
}
expected = flatten(nested)
print(expected)
output
{'A:B': {'C': 'C val', 'G': 'G val'}, 'A:D:E': {'F': 'F val'}}