Set the value of a nested dict of unknown depth? [duplicate] - python

This question already has answers here:
Access nested dictionary items via a list of keys?
(20 answers)
Closed 5 years ago.
So I have a list which acts as a 'map' of sorts, and a dict which is pulled from a JSON file.
For this example, lets say they look like this:
L = ['One', 'Two', 'Three']
D = {
"One": {
"Two": {
"Three": {
"Four": "String"
}
}
}
}
I need a function for my game.
I will be passing it two things: L and a Object.
Note that L just leads to "Three" so thats what I want to replace. Though, I could be replacing "Four" or any other element at an unknown debth.
What I'm replacing could be a string, list, or dict, and I could be replacing it with any other object, including None, or a bool.
The structure of D needs to remain intact, besides the value being replaced.

This function will take a list of strings as a path and an object to replace the located value with, operating on a global variable nested_dicts. If you want to adhere to a functional paradigm, you might want to rewrite it accordingly.
def replace (path, replacement):
pointer = nested_dicts
try:
for key in path[:-1]:
pointer = pointer[key]
except KeyError:
return False
pointer[path[-1]] = replacement
return True

Something like this works (path is L, o is the replacement):
L = ['One', 'Two', 'Three']
D = {
"One": {
"Two": {
"Three": {
"Four": "String"
}
}
}
}
def replace(path, o):
cur = D
for k in path[:-1]:
cur = cur[k]
cur[path[-1]] = o
replace(L, {'msg': 'good'})
print(D)

Related

Appending parameters from a list in a for loop

I have a list with several data, ['a','b','c'...]
The goal here is to read all the items in the list and access to a json file to retrieve that information.
The json i have is as follows
{
"a": {
"b": {
"c": { .... }
}
} }
So, the final sentence to execute is
code.get(list[0]).get(list[1]).get(list[2]...get(list[n]
Is there any way i can do a for loop based on the length of the list to do this?
Something like, for any item in the list, append a ..get(list[i]) to my sentence
Thanks
Just iterate over the key list and go down into the code dict:
keys = ['a','b','c']
current_level = code # Top level
for key in keys:
current_level = current_level.get(key) # Descent next level
print current_level # Value of the 'c' dict

python create list of dictionaries without reference

I have a requirement in which I need create dictionary objects with duplicate keys embedded into a list object, something like this:
[{ "key": "ABC" },{ "key": "EFG" } ]
I decided to have a top level list initialized to empty like outer_list=[] and a placeholder dictionary object like dict_obj= {}. Next I keep adding elements to my list using the following steps:
assign { "key": "ABC" } to dict_obj using dict_obj["key"]="ABC"
Add this object to the list using outer_list.append(dict_obj)
Flush/pop the key/items in dictionary object using dict_obj.clear()
Repeat steps 1 to 3 based on the number of key/item combinations in my data
Issue: the outer_list object maintains a reference to the original dict_obj and if the dict_obj is flushed or a new key/item is added it changes accordingly. So finally, I end up with this [{ "key": "EFG" },{ "key": "EFG" } ] instead of [{ "key": "ABC" },{ "key": "EFG" } ]
Please guide me with some workarounds if possible.
I think there are two ways to avoid the duplicate references.
The first is to append a copy of the dictionary, instead of a reference to it. dict instances have a copy method, so this is easy. Just change your current append call to:
outer_list.append(dict_obj.copy())`
The other option is to not always use the same dict_obj object, but rather create a separate dictionary object for each entry. In this version, you'd replace your call to dict_obj.clear() with:
dict_obj = {}
For the second approach, you might choose to reorder things rather than doing a straight one-line replacement. You could move the setup code to the start of the loop and get rid of the reset logic at the end of the loop.
That is, change code that looks like this:
outer_list = []
dict_obj = {}
for foo in whatever:
# add stuff to dict_obj
outer_list.append(dict_obj)
dict_obj.clear()
To:
outer_list = []
for foo in whatever:
dict_obj = {}
# add stuff to dict_obj
outer_list.append(dict_obj)
If the logic for creating the inner dictionaries is simple enough to compute, you might even turn the whole thing into a list comprehension:
outer_list = [{"key": value, "key2": value2} for value, value2 in some_sequence]
The following should be self-explanatory:
# object reuse
d = {}
l = []
d['key'] = 'ABC'
l.append(d)
d.clear()
print(l) # [{}] : cleared!
d['key'] = 'EFG'
l.append(d)
print(l) # [{'key': 'EFG'}, {'key': 'EFG'}]
# explicit creation of new objects
d = {}
l = []
d['key'] = 'ABC'
l.append(d)
print(l)
d = {}
d['key'] = 'EFG'
l.append(d)
print(l)

How to remove dictionary's keys and values based on another dictionary?

I wish to remove keys and values in one JSON dictionary based on another JSON dictionary's keys and values. In a sense I am looking perform a "subtraction". Let's say I have JSON dictionaries a and b:
a = {
"my_app":
{
"environment_variables":
{
"SOME_ENV_VAR":
[
"/tmp",
"tmp2"
]
},
"variables":
{ "my_var": "1",
"my_other_var": "2"
}
}
}
b = {
"my_app":
{
"environment_variables":
{
"SOME_ENV_VAR":
[
"/tmp"
]
},
"variables":
{ "my_var": "1" }
}
}
Imagine you could do a-b=c where c looks like this:
c = {
"my_app":
{
"environment_variables":
{
"SOME_ENV_VAR":
[
"/tmp2"
]
},
"variables":
{ "my_other_var": "2" }
}
}
How can this be done?
You can loop through your dictionary using for key in dictionary: and you can delete keys using del dictionary[key], I think that's all you need. See the documentation for dictionaries: https://docs.python.org/2/tutorial/datastructures.html#dictionaries
The way you can do it is to:
Create copy of a -> c;
Iterate over every key, value pair inside b;
Check if for same top keys you have same inner keys and values and delete them from c;
Remove keys with empty values.
You should modify code, if your case will be somehow different (no dict(dict), etc).
print(A)
print(B)
C = A.copy()
# INFO: Suppose your max depth is as follows: "A = dict(key:dict(), ...)"
for k0, v0 in B.items():
# Look for similiar outer keys (check if 'vars' or 'env_vars' in A)
if k0 in C:
# Look for similiar inner (keys, values)
for k1, v1 in v0.items():
# If we have e.g. 'my_var' in B and in C and values are the same
if k1 in C[k0] and v1 == C[k0][k1]:
del C[k0][k1]
# Remove empty 'vars', 'env_vars'
if not C[k0]:
del C[k0]
print(C)
{'environment_variables': {'SOME_ENV_VAR': ['/tmp']},
'variables': {'my_var': '2', 'someones_var': '1'}}
{'environment_variables': {'SOME_ENV_VAR': ['/tmp']},
'variables': {'my_var': '2'}}
{'variables': {'someones_var': '1'}}
The following does what you need:
def subtract(a, b):
result = {}
for key, value in a.items():
if key not in b or b[key] != value:
if not isinstance(value, dict):
if isinstance(value, list):
result[key] = [item for item in value if item not in b[key]]
else:
result[key] = value
continue
inner_dict = subtract(value, b[key])
if len(inner_dict) > 0:
result[key] = inner_dict
return result
It checks if both key and value are present. It could del items, but I think is much better to return a new dict with the desired data instead of modifying the original.
c = subtract(a, b)
UPDATE
I have just updated for the latest version of the data provided by in the question. Now it 'subtract' list values as well.
UPDATE 2
Working example: ipython notebook

How do I find an item in an array of dictionaries?

Suppose I have this:
list = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
I need to find p2 and get its value.
You can try the following ... That will return all the values equivilant to the givenKey in all dictionaries.
ans = [d[key] for d in list if d.has_key(key)]
If this is what your actual code looks like (each key is unique), you should just use one dictionary:
things = { 'p1':'v1', 'p2':'v2', 'p3':'v3' }
do_something(things['p2'])
You can convert a list of dictionaries to one dictionary by merging them with update (but this will overwrite duplicate keys):
dict = {}
for item in list:
dict.update(item)
do_something(dict['p2'])
If that's not possible, you'll need to just loop through them:
for item in list:
if 'p2' in item:
do_something(item['p2'])
If you expect multiple results, you can also build up a list:
p2s = []
for item in list:
if 'p2' in item:
p2s.append(item['p2'])
Also, I wouldn't recommend actually naming any variables dict or list, since that will cause problems with the built-in dict() and list() functions.
These shouldn't be stored in a list to begin with, they should be stored in a dictionary. Since they're stored in a list, though, you can either search them as they are:
lst = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
p2 = next(d["p2"] for d in lst if "p2" in d)
Or turn them into a dictionary:
dct = {}
any(dct.update(d) for d in lst)
p2 = dct["p2"]
You can also use this one-liner:
filter(lambda x: 'p2' in x, list)[0]['p2']
if you have more than one 'p2', this will pick out the first; if you have none, it will raise IndexError.
for d in list:
if d.has_key("p2"):
return d['p2']
If it's a oneoff lookup, you can do something like this
>>> [i['p2'] for i in my_list if 'p2' in i]
['v2']
If you need to look up multiple keys, you should consider converting the list to something that can do key lookups in constant time (such as a dict)
>>> my_list = [ { 'p1':'v1' } ,{ 'p2':'v2' } ,{ 'p3':'v3' } ]
>>> my_dict = dict(i.popitem() for i in my_list)
>>> my_dict['p2']
'v2'
Start by flattening the list of dictionaries out to a dictionary, then you can index it by key and get the value:
{k:v for x in list for k,v in x.iteritems()}['p2']

Python dict restructure

Given the following data structure:
out = {
'foo': { 'public':{}, 'private':{}, 'other':{} },
'bar': { 'public':{}, 'private':{}, 'other':{} }
}
I am attempting to slice out parts of the sub-structure to create a new dict. My use for this is to respond to requests with all data except that marked private.
To do the opposite is trivial:
response = {x,y['private'] for x,y in out.iteritems()}
Which constructs a dict for each foo and bar containing only the data marked private. But is there some functionality in the standard library (itertools perhaps) that would produce the following:
out = {
'foo': { 'public':{}, 'other':{} },
'bar': { 'public':{}, 'other':{} }
}
I have tried the following:
{x:(y['public'], y['other']) for x,y in out.iteritems()}
Although I would prefer to not use a tuple, and not explicitly name each sub-structure, as this is not reusable or scalable.
def remove(name, obj):
return {x:y for x,y in obj.iteritems() if x is not name}
{x:remove('private',y) for x,y in out.iteritems()}
This seems to work, but is there a better way? Any ideas?
You can break this down into parts; you want a new dictionary which has some parts removed. So create a function which can return a dictionary without the elements in question and call this is part of an iterator.
You're using dictionary comprehensions so something like this would work:
def remove_items(d, *items):
"""
Return dictionary copy with some items removed.
"""
return { a: b for a, b in d.iteritems() if a not in items }
print { x: remove_items(y, 'private') for x, y in out.iteritems() }
Would that be what you mean?
respose = {x:{'public': y['public'], 'other': y['other']} for x,y in out.iteritems()}
Try this:
response = {}
for x,y in out.iteritems():
response[x] = dict(y)
del response[x]['private']
If you don't mind destroying the original dictionary then just iterate over it del'ing the 'private' elements, otherwise you need to copy the second level dicts and then del the unwanted items.

Categories