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'}}
Related
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'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'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_'}}}
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'}}
I would like to build a multi-level dictionary such like:
A = {
'a': {
'A': {
'1': {},
'2': {},
},
'B': {
'1': {},
'2': {},
},
},
'b': {
'A': {
'1': {},
'2': {},
},
'B': {
'1': {},
'2': {},
},
},
}
My question is that whether it existed a function that I can build the above diction by:
D = function(['a', 'b'], ['A', 'B'], ['1', '2'], {})
This uses the copy function to allow you specify a different leaf node. Otherwise all the leaves will point to the same dictionary.
from copy import copy
def multidict(*args):
if len(args) == 1:
return copy(args[0])
out = {}
for x in args[0]:
out[x] = multidict(*args[1:])
return out
print multidict(['a', 'b'], ['A', 'B'], ['1', '2'], {})
def multi(*args):
if len(args) > 1:
return {arg:multi(*args[1:]) for arg in args[0]}
else:
return args[0]
multi(['a', 'b'], ['A', 'B'], ['1', '2'], {})
returns
{'a': {'A': {'1': {}, '2': {}}, 'B': {'1': {}, '2': {}}},
'b': {'A': {'1': {}, '2': {}}, 'B': {'1': {}, '2': {}}}}
EDIT: In my solution, the last argument {} will be copied into every leaf of the output as a reference to the same dictionary. If this is a problem (replacing it with an immutable object, such as float, integer or string is a different thing), use the copy.copy idea of #matt.
It's easy to write using recusion
def multi_level_dict(*args):
x = dict()
if args:
for k in args[0]:
x[k] = multi_level_dict(*args[1:])
return x
your case would be
multi_level_dict(["a", "b"], ["A", "B"], ["1", "2"])
or even
multi_level_dict("ab", "AB", "12")
dict comprehension is a cool approach for that, but only if you nesting depth is fixed:
{x:{y:{z:{} for z in ['1', '2']} for y in 'AB'} for x in 'ab'}