I need to manipulate dynamic a dictionary on python. I have unrecognized information from input information, as in this example:
'properties[props][defaultValue]': ''
'properties[props][dt_precision]': ''
'properties[props][dt_table]': ''
'properties[props][dtfield]': ''
I need to convert to a dictionary like this example:
properties['props']['dt_table'] = 1
properties['props']['dt_table'] = 2
I don't know the real information, but I know that the format is like this:
variable[index] = value
variable[index][index_1] = value
variable[index][index_1] [index_2]= value
variable[index][index_1] [index_2][index_3]= value
My problem is, how can I add a dictionary with infinite layers of keys? In others words, add a large hierarchy of subkeys to subkeys dynamically.
In javascript I use references like this:
f=var['key'];
f['key'] = {};
f = f['key'];
f['key'] = 120;
Which allows me to construct:
var['key']['key'] = 120
but the equivalent in python does not work.
Naive approach
The simplest approach, involves creating new dictionary on each sub-level by hand:
var = {}
var['key'] = {}
var['key']['key'] = 120
print(var['key']['key'])
print(var)
Which gives following output:
120
{'key': {'key': 120}}
Autovivification
You can automate it by using defaultdict as suggested by #martineau in comments:
from collections import defaultdict
def tree():
return defaultdict(tree)
v2 = tree()
v2['key']['key'] = 120
print(v2['key']['key'])
print(v2)
With output:
120
defaultdict(<function tree at 0x1ae7d88>, {'key': defaultdict(<function tree at 0x1ae7d88>, {'key': 120})})
Related
I have a dictionary of zoo animals. I want to put it into the dictionary in a nested dictionary but get a KeyError because that particular species has not been added to the dictionary.
def add_to_world(self, species, name, zone = 'retreat'):
self.object_attr[species][name] = {'zone' : zone}
Is there a shortcut to checking if that species is in the dictionary and create it if it is not or do i have to do it the long way and manually check if that species has been added?
def add_to_world(self, species, name, zone = 'retreat'):
self.object_attr.setdefault(species, {})[name] = {'zone' : zone}
Here's an example of using defaultdict with a dictionary as a value.
>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> d["species"]["name"] = {"zone": "1"}
>>> d
defaultdict(<type 'dict'>, {'species': {'name': {'zone': '1'}}})
>>>
If you want further nesting you'll need to make a function to return defaultdict(dict).
def nested_defaultdict():
return defaultdict(dict)
# Then you can use a dictionary nested to 3 levels
d2 = defaultdict(nested_defaultdict)
d2["species"]["name"]["zone"] = 1
Autovivification of dictionary values can be performed by collections.defaultdict.
Is it possible to create a dynamic linked variable? So that changes in original_VAR will automatically take effect in copied_VAR ? Like so:
original_VAR = 'original_VAL'
copied_VAR = original_VAR
original_VAR = 'modified_VAL'
print(copied_VAR)
#desired output:
>>>> 'modified_VAL'
A similar behavior can be created for lists under few conditions:
original_DICT_ARR = [{'key': 'original_VAL'}]
# 1 - does not create a dynamic link
copied_DICT_ARR = [value for value in original_DICT_ARR]
# 2 - does create a dynamic link
copied_DICT_ARR = original_DICT_ARR
# 3 - does create a dynamic link, if the copied element is a list or dict, but not if string, boolean, int, float
copied_DICT_ARR = []
copied_DICT_ARR.append(original_DICT_ARR[0])
# MODIFICATION:
original_DICT_ARR[0]['key'] = 'modified_VAL'
# RESULT for 2,3
print(copied_DICT_ARR[0])
>>>> {'key': 'modified_VAL'}
Why would I want to do this?
I am building a list, the list is full of dict objects. I need to assign a value to a certain dict key.
Later, that value might change - I don't want to loop through all dictionaries in the list again. I want to change the original variable, and have the effect taken place in all dictionaries automatically.
You can keep a reference the specific dict you want to modify later. Since it refers to the same underlying dict - changes will be reflected in your reference. Like so:
original_DICT_ARR = [{'key': 'original_VAL'}, {'key': 'another_val'}]
target_dict = original_DICT_ARR[0]
# MODIFICATION:
original_DICT_ARR[0]['key'] = 'modified_VAL'
# RESULT for 2,3
print(target_dict)
Gives:
{'key': 'modified_VAL'}
You could achieve this and keep things abstract by using a mutable object and subclassing UserDict and overriding __getitem__:
class ChangingVal:
def __init__(self, val):
self.val = val
class ChangingValsDict(UserDict):
def __getitem__(self, key):
ret = self.data[key]
if isinstance(ret, ChangingVal):
ret = ret.val
return ret
my_dict = ChangingValsDict()
changing_val = ChangingVal(3)
my_dict["changing_value"] = changing_val
print(my_dict["changing_value"]) # -- outputs "3"
# change your value
changing_val.val = 6
print(my_dict["changing_value"]) # -- outputs "6"
I was attempting to add an attribute to a pre-existing object in a dictionary:
key = 'key1'
dictObj = {}
dictObj[key] = "hello world!"
#attempt 236 (j/k)
dictObj[key]["property2"] = "value2" ###'str' object does not support item assignment
#another attempt
setattr(dictObj[key], 'property2', 'value2') ###'dict' object has no attribute 'property2'
#successful attempt that I did not like
dictObj[key] = {'property':'value', 'property2':''} ###instantiating the dict object with all properties defined seemed wrong...
#this did allow for the following to work
dictObj[key]["property2"] = "value2"
I tried various combinations (including setattr, etc.) and was not having much luck.
Once I have added an item to a Dictionary, how can I add additional key/value pairs to that item (not add another item to the dictionary).
As I was writing up this question, I realized my mistake.
key = 'key1'
dictObj = {}
dictObj[key] = {} #here is where the mistake was
dictObj[key]["property2"] = "value2"
The problem appears to be that I was instantiating the object with key 'key1' as a string instead of a dictionary. As such, I was not able to add a key to a string. This was one of many issues I encountered while trying to figure out this simple problem. I encountered KeyErrors as well when I varied the code a bit.
Strictly reading the question, we are considering adding an attribute to the object. This can look like this:
class DictObj(dict):
pass
dictObj = DictObj(dict)
dictObj.key = {'property2': 'value2'}
And then, we can use dictObj.key == {'property2': 'value2'}
Given the context of the question, we are dealing with adding a property to the dictionary. This can be done (in addition to #John Bartels's approach) in the following ways:
1st option - add the "full" content in one line:
dictObj = {'key': {'property2': 'value2'}}
2nd option for the case of dictionary creation with initial values:
dictObj = dict(key = dict(property2 = 'value2'))
3rd option (Python 3.5 and higher):
dictObj = {}
dictObj2 = {'key': {'property2': 'value2'}}
dictObj = {**dictObj, **dictObj2}
4th option (Python 3.9 and higher):
dictObj = {}
dictObj |= {'key': {'property2': 'value2'}}
In all cases the result will be: dictObj == {'key': {'property2': 'value2'}}
I am have the following dict:
abc = {"type":"insecure","id":"1","name":"peter"}
what I want to do is to have a new dict based on the old dict in which there is no key "type" and key "id" is changed to "identity". The new dict will look as follows:
xyz = {"identity":"1","name":"peter"}
The solution that I came up was as follows:
abc = {"type":"insecure","id":"1","name":"peter"}
xyz = {}
black_list_values = set(("type","id"))
for k in abc:
if k not in blacklist_values:
xyz[k] = abc[k]
xyz["identity"] = abc["id"]
I was wondering if its the fastest and efficient way to do that? Right now, "abc" have only three values. If "abc" is much bigger and have many values then is my solution still the efficient and fast.
You can use a dict-comprehension:
abc = {"type":"insecure","id":"1","name":"peter"}
black_list = {"type"}
rename ={"id":"identity"} #use a mapping dictionary in case you want to rename multiple items
dic = {rename.get(key,key) : val for key ,val in abc.items() if key not in black_list}
print dic
output:
{'name': 'peter', 'identity': '1'}
You want to create a new dictionary anyway. You can iterate over keys/values in a dict comprehension, which is more compact, but functionally the same:
abc = {"type":"insecure","id":"1","name":"peter"}
black_list_values = set(("type","id"))
xyz = {k:v for k,v in abc.iteritems() if k not in black_list_values}
xyz["identity"] = abc["id"]
Without iterating through the original dict:
abc = {"type":"insecure","id":"1","name":"peter"}
xyz = abc.copy()
xyz.pop('type')
xyz['identity'] = xyz.pop('id')
If all the keys are pre-known and it's a short list of keys, then the obvious solution is just
xyz = {"identity":abc["id"],"name":abc["name"]}
A another simple suggestion would be to use the dict() function:
abc = {"type":"insecure","id":"1","name":"peter"}
xyz = dict(abc)
Then perform the replacement in any way you see fit =-)
I'm trying to take a list of objects and rearrange them into a dictionary of nested dictionaries based on the objects' "keys." Each key contains the key of its parent dictionary in a known pattern. The problem I'm running into is being able to access an element like...
hier_data['1']['Assets']['1.2']['Assets']['1.2.3']['Assets']['1.2.3.4']['Assets']
...dynamically so as to add to it. Is there a way to build some sort of recursive function that will traverse down the dictionary based on the key? For example, if I needed to add the object with the key '1.2.3.4.5', is there a way to do...
hier_data['1']['Assets']['1.2']['Assets']['1.2.3']['Assets']['1.2.3.4']['Assets']['1.2.3.4.5'] = {...}
...dynamically and recursively?
I should note that the list I'm starting from is sorted by key so that I should always have the current object's parent's 'Assets' dictionary available for adding to.
You can use a recursive defaultdict:
>>> from collections import defaultdict
>>> l = lambda: defaultdict(l)
>>> d = defaultdict(l)
>>> d['123']['4234']['asd']['dsaf'] = 4
>>> d
defaultdict(<function <lambda> at 0x15f9578>, {'123': defaultdict(<function <lambda> at 0x15f9578>, {'4234': defaultdict(<function <lambda> at 0x15f9578>, {'asd': defaultdict(<function <lambda> at 0x15f9578>, {'dsaf': 4})})})})
Turns out what I was having trouble with was simpler than I thought. All I needed to do was something like this:
hier_data = {}
for id in sorted(data.iterkeys()):
key = id.split('.')
data[id]['Assets'] = {}
insert_point = hier_data
for i in range(len(key)/2-1):
insert_point = insert_point['.'.join(key[0:2*i+2])]['Assets']
insert_point[id] = data[id]
return hier_data
I thought getting keys from dictionaries (e.g. hier_data[...]) would return a copy of the object at that point in the dictionary, not a pointer to the object. Turns out all I needed was to iterate over my broken-up key to move my insert_point cursor to the correct spot to add in my object.