How to append to a nested dictionary in python - python

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})

Related

How do I append a value to dict key? (AttributeError: 'str' object has no attribute 'append')

Say I have a dictionary with one key (and a value):
dict = {'key': '500'}.
Now I want to add a new value '1000' to the same key. However,
dict[key].append('1000')
just gives me "AttributeError: 'str' object has no attribute 'append'".
If I do
dict[key] = '1000'
it replaces the previous value.
I'm guessing I have to create a list as a value and somehow append that list as the key's value but I'm not sure how I would go about this. Thanks for any help!
I suggest the usage of a defaultdict that instantiates an empty list when a key is missing.
>>> from collections import defaultdict
>>> d = defaultdict(list)
>>> d['key'].append(500)
>>> d
defaultdict(<type 'list'>, {'key': [500]})
>>> d['key'].append(1000)
>>> d
defaultdict(<type 'list'>, {'key': [500, 1000]})
I don't recommend having strings/integers as values and then switching to lists once you want to append to a field. Keep it consistent.

python dictionary update methods to extend dictionary

So i am using dictionary within a dictionary and every time I would try to extend the child_dict, which i do using a loop, only the last iteration value persists while the previous ones are over-written
parent_dict = defaultdict(list)
for getdata from datasource:
# I generate 'child_dict' here
parent_dict[parentDict_key] = child_dict
I tried to use .update(child_dict) method but it gives me
AttributeError: 'list' object has no attribute 'update'
I also tried to use .append() method but it makes the parent a list of dictionaries.
Is there any better way to add new child_dict to my parent_dict and just extend it during each iteration?
Well, if you want your values to be dictionaries and not lists you should use:
parent_dict = defaultdict(dict)
instead of:
parent_dict = defaultdict(list)
and then to generate:
parent_dict[parentDict_key][child_dict_key] = child_dict_value

Python - Add to a dictionary using a string

[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

Recursively Access Item In Python Dictionary of Unknown Dimensions

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.

Python dictionary : TypeError: unhashable type: 'list'

I'm having troubles in populating a python dictionary starting from another dictionary.
Let's assume that the "source" dictionary has string as keys and has a list of custom objects per value.
I'm creating my target dictionary exactly as I have been creating my "source" dictionary how is it possible this is not working ?
I get
TypeError: unhashable type: 'list'
Code :
aTargetDictionary = {}
for aKey in aSourceDictionary:
aTargetDictionary[aKey] = []
aTargetDictionary[aKey].extend(aSourceDictionary[aKey])
The error is on this line : aTargetDictionary[aKey] = []
The error you gave is due to the fact that in python, dictionary keys must be immutable types (if key can change, there will be problems), and list is a mutable type.
Your error says that you try to use a list as dictionary key, you'll have to change your list into tuples if you want to put them as keys in your dictionary.
According to the python doc :
The only types of values not acceptable as keys are values containing
lists or dictionaries or other mutable types that are compared by
value rather than by object identity, the reason being that the
efficient implementation of dictionaries requires a key’s hash value
to remain constant
This is indeed rather odd.
If aSourceDictionary were a dictionary, I don't believe it is possible for your code to fail in the manner you describe.
This leads to two hypotheses:
The code you're actually running is not identical to the code in your question (perhaps an earlier or later version?)
aSourceDictionary is in fact not a dictionary, but is some other structure (for example, a list).
As per your description, things don't add up. If aSourceDictionary is a dictionary, then your for loop has to work properly.
>>> source = {'a': [1, 2], 'b': [2, 3]}
>>> target = {}
>>> for key in source:
... target[key] = []
... target[key].extend(source[key])
...
>>> target
{'a': [1, 2], 'b': [2, 3]}
>>>
It works fine : http://codepad.org/5KgO0b1G,
your aSourceDictionary variable may have other datatype than dict
aSourceDictionary = { 'abc' : [1,2,3] , 'ccd' : [4,5] }
aTargetDictionary = {}
for aKey in aSourceDictionary:
aTargetDictionary[aKey] = []
aTargetDictionary[aKey].extend(aSourceDictionary[aKey])
print aTargetDictionary
You can also use defaultdict to address this situation. It goes something like this:
from collections import defaultdict
#initialises the dictionary with values as list
aTargetDictionary = defaultdict(list)
for aKey in aSourceDictionary:
aTargetDictionary[aKey].append(aSourceDictionary[aKey])

Categories