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.
Related
I have this kind of dictionary:
{"Odds":{"Home-Win": {"Name-BookMaker":{A:value}}}
This structure is saved inside my pickle and I want to access the element called value, with the for loop iterate over the pickle:
for match in name_of_the_pickle:
odds = match.get("Odds")
home_win = odds.get("Home-Win").values()
but with this last instruction my output is the following:
"dict_values([{A:value}])"
But what I want is only "value", how can I do this?
Ok I found a working example for te instance you gave us based on casting the dict_values object to a list:
odds = match.get("Odds")
home_win = list(odds.get("Home-Win").values())[0]
At this point home_win looks like this:
{'A': 'value'}
Then to retrieve 'value', you could call values() again:
val = list(home_win.values())[0]
print(val)
>>>'value'
I have the following nested dictionary:
d = {'A':{'a':1}, 'B':{'b':2}}
I want to add values to d without overwriting.
So if I want to append the value ['A', 'b', 3] the dictionary should read:
d = {'A':{'a':1, 'b':3}, 'B':{'b':2}}
d['A'].append({'b':3}) errors with:
AttributeError: 'dict' object has no attribute 'append'
I don't know what the nested dictionary will be in advance. So saying:
d['A'] = {'a':1, 'b':3}
will not work for my case as I am "discovering/calculating" the values as the script runs.
Thanks
In python, append is only for lists, not dictionaries.
This should do what you want:
d['A']['b'] = 3
Explanation: When you write d['A'] you are getting another dictionary (the one whose key is A), and you can then use another set of brackets to add or access entries in the second dictionary.
You're looking for the update method:
d['A'].update({'b':3})
[Python 3.4.2]
I know this question sounds ridiculous, but I can't figure out where I'm messing up. I'm trying to add keys and values to a dictionary by using strings instead of quoted text. So instead of this,
dict['key'] = value
this:
dict[key] = value
When I run the command above, I get this error:
TypeError: 'str' object does not support item assignment
I think Python is thinking that I'm trying to create a string, not add to a dictionary. I'm guessing I'm using the wrong syntax. This is what I'm trying to do:
dict[string_for_key][string_for_value] = string_for_deeper_value
I want this^ command to do this:
dict = {string_for_key: string_for_value: string_for_deeper_value}
I'm getting this error:
TypeError: 'str' object does not support item assignment
I should probably give some more context. I'm:
creating one dictionary
creating a copy of it (because I need to edit the dictionary while iterating through it)
iterating through the first dictionary while running some queries
trying to assign a query's result as a value for each "key: value" in the dictionary.
Here's a picture to show what I mean:
key: value: query_as_new_value
-----EDIT-----
Sorry, I should have clarified: the dictionary's name is not actually 'dict'; I called it 'dict' in my question to show that it was a dictionary.
-----EDIT-----
I'll just post the whole process I'm writing in my script. The error occurs during the last command of the function. Commented out at the very bottom are some other things I've tried.
from collections import defaultdict
global query_line, pericope_p, pericope_f, pericope_e, pericope_g
def _pre_query(self, typ):
with open(self) as f:
i = 1
for line in f:
if i == query_line:
break
i += 1
target = repr(line.strip())
###skipping some code
pericope_dict_post[self][typ] = line.strip()
#^Outputs error TypeError: 'str' object does not support item assignment
return
pericope_dict_pre = {'pericope-p.txt': 'pericope_p',
'pericope-f.txt': 'pericope_f',
'pericope-e.txt': 'pericope_e',
'pericope-g.txt': 'pericope_g'}
pericope_dict_post = defaultdict(dict)
#pericope_dict_post = defaultdict(list)
#pericope_dict_post = {}
for key, value in pericope_dict_pre.items():
pericope_dict_post[key] = value
#^Works
#pericope_dict_post.update({key: value})
#^Also works
#pericope_dict_post.append(key)
#^AttributeError: 'dict' object has no attribute 'append'
#pericope_dict_post[key].append(value)
#^AttributeError: 'dict' object has no attribute 'append'
_pre_query(key, value)
-----FINAL EDIT-----
Matthias helped me figure it out, although acushner had the solution too. I was trying to make the dictionary three "levels" deep, but Python dictionaries cannot work this way. Instead, I needed to create a nested dictionary. To use an illustration, I was trying to do {key: value: value} when I needed to do {key: {key: value}}.
To apply this to my code, I need to create the [second] dictionary with all three strings at once. So instead of this:
my_dict[key] = value1
my_dict[key][value1] = value2
I need to do this:
my_dict[key][value1] = value2
Thanks a ton for all your help guys!
You could create a dictionary that expands by itself (Python 3 required).
class AutoTree(dict):
"""Dictionary with unlimited levels"""
def __missing__(self, key):
value = self[key] = type(self)()
return value
Use it like this.
data = AutoTree()
data['a']['b'] = 'foo'
print(data)
Result
{'a': {'b': 'foo'}}
Now I'm going to explain your problem with the message TypeError: 'str' object does not support item assignment.
This code will work
from collections import defaultdict
data = defaultdict(dict)
data['a']['b'] = 'c'
data['a'] doesn't exist, so the default value dict is used. Now data['a'] is a dict and this dictionary gets a new value with the key 'b' and the value 'c'.
This code won't work
from collections import defaultdict
data = defaultdict(dict)
data['a'] = 'c'
data['a']['b'] = 'c'
The value of data['a'] is defined as the string 'c'. Now you can only perform string operations with data['a']. You can't use it as a dictionary now and that's why data['a']['b'] = 'c' fails.
first, do not use dict as your variable name as it shadows the built-in of the same name.
second, all you want is a nested dictionary, no?
from collections import defaultdict
d = defaultdict(dict)
d[string_for_key][string_for_value] = 'snth'
another way, as #Matthias suggested, is to create a bottomless dictionary:
dd = lambda: defaultdict(dd)
d = dd()
d[string_for_key][string_for_value] = 'snth'
you can do something like this:
>>> my_dict = {}
>>> key = 'a' # if key is not defined before it will raise NameError
>>> my_dict[key] = [1]
>>> my_dict[key].append(2)
>>> my_dict
{'a': [1, 2]}
Note: dict is inbuilt don't use it as variable name
I have a nested Python Dict and I am trying to take values from a list and then iterate them into a Dict's values as such:
for row in rows:
Dict[A][AA][AAA] += 1
However, when I print my dict, it appears to be adding all of the increments to all of the Dict entries. By which I mean that instead of this:
{KeyA:{KeyAA:{KeyAAA:5}}}
{KeyB:{KeyBB:{KeyBBB:10}}}
I am getting this:
{KeyA:{KeyAA:{KeyAAA:15}}}
{KeyB:{KeyBB:{KeyBBB:15}}}
I'm a bit stumped.
EDIT:
This is how the Dicts were created:
I first skim through a long table that contains a type classification. While I'm doing that, I create a new entry into the main Dict. At the same time, I'm collecting all of the unique classifications into a subDict so that I can add this to the main Dict later on:
Dict = {}
subDict = {}
for row in skimRows:
Dict[row[0]] = {"Type":row[1],"Assoc":{}} # Save each ID and origin Type to Dict
if item not in subDict: # Check to see if unique item already exists in subDict
subDict[item] = 0
Here is evidently where I was going wrong. I was then taking the subDict and plunking this into the main Dict, not realising the inserted subDict was retaining its relationship to the original subDict object:
for key in Dict: # After initial iteration and Type collection, add new subDict to each Dict key
Dict[key]["Assoc"] = subDict
SOLUTION:
Per the correct answer below, I fixed it by adding .copy()
for key in Dict: # After initial iteration and Type collection, add new subDict to each Dict key
Dict[key]["Assoc"] = subDict.copy()
Your innermost dictionaries are shared, not unique objects:
>>> somedict = {}
>>> somedict['foo'] = {'bar': 0}
>>> somedict['spam'] = somedict['foo']
>>> somedict['foo']['bar'] += 1
>>> somedict['spam']
{'bar': 1}
>>> somedict['foo'] is somedict['spam']
True
The two keys foo and spam both are referring to the same object here, one dictionary object holding a key bar.
You should not reuse your dictionaries like this. Either create a new empty dictiorary:
somedict['spam'] = {'bar': 0}
or create a (shallow) copy:
somedict['spam'] = somedict['foo'].copy()
This question already has answers here:
Is there a standard class for an infinitely nested defaultdict?
(6 answers)
Closed 9 years ago.
I'm creating a dictionary structure that is several levels deep. I'm trying to do something like the following:
dict = {}
dict['a']['b'] = True
At the moment the above fails because key 'a' does not exist. At the moment I have to check at every level of nesting and manually insert an empty dictionary. Is there some type of syntactic sugar to be able to do something like the above can produce:
{'a': {'b': True}}
Without having to create an empty dictionary at each level of nesting?
As others have said, use defaultdict. This is the idiom I prefer for arbitrarily-deep nesting of dictionaries:
def nested_dict():
return collections.defaultdict(nested_dict)
d = nested_dict()
d[1][2][3] = 'Hello, dictionary!'
print(d[1][2][3]) # Prints Hello, dictionary!
This also makes checking whether an element exists a little nicer, too, since you may no longer need to use get:
if not d[2][3][4][5]:
print('That element is empty!')
This has been edited to use a def rather than a lambda for pep8 compliance. The original lambda form looked like this below, which has the drawback of being called <lambda> everywhere instead of getting a proper function name.
>>> nested_dict = lambda: collections.defaultdict(nested_dict)
>>> d = nested_dict()
>>> d[1][2][3]
defaultdict(<function <lambda> at 0x037E7540>, {})
Use defaultdict.
Python: defaultdict of defaultdict?
Or you can do this, since dict() function can handle **kwargs:
http://docs.python.org/2/library/functions.html#func-dict
print dict(a=dict(b=True))
# {'a': {'b' : True}}
If the depth of your data structure is fixed (that is, you know in advance that you need mydict[a][b][c] but not mydict[a][b][c][d]), you can build a nested defaultdict structure using lambda expressions to create the inner structures:
two_level = defaultdict(dict)
three_level = defaultdict(lambda: defaultdict(dict))
four_level = defaultdict(lamda: defaultdict(lambda: defaultdict(dict)))