How to create multi-level dictionaries easily - python

I want to create a nested dictionaries like the example below:
rule={}
rule["exports-rule-info-2"]["pathname"] = qtree_name
rule["exports-rule-info-2"]["security-rules"]["security-rule-info"]["read-write"]["exports-hostname-info"]["name"] = security_rules
rule["exports-rule-info-2"]["security-rules"]["security-rule-info"]["root"]["exports-hostname-info"]["name"] = security_rules
rule["exports-rule-info-2"]["security-rules"]["security-rule-info"]["sec-flavor"]["sec-flavor-info"]["flavor"] = 'sec'

You can use collections.defaultdict for this. A defaultdict is given some function used for initializing missing values. In your case, use a recursive defaultdict of defaultdict.
import collections
infinitedict = lambda: collections.defaultdict(infinitedict)
rule = infinitedict()
After putting your stuff into the defaultdict, the result will look somewhat like this (though not exactly like it, due to defaultdict slightly different representation):
{'exports-rule-info-2': {
'security-rules': {
'security-rule-info': {
'read-write': {'exports-hostname-info': {'name': 'security_rules '}},
'root': {'exports-hostname-info': {'name': 'security_rules'}},
'sec-flavor': {'sec-flavor-info': {'flavor': 'sec'}}}},
'pathname': 'qtree_name'}}

from collections import defaultdict
tree = lambda: defaultdict(tree)
rule = tree()
rule["exports-rule-info-2"]["pathname"] = qtree_name
rule["exports-rule-info-2"]["security-rules"]["security-rule-info"]["read-write"]["exports-hostname-info"]["name"] = security_rules
rule["exports-rule-info-2"]["security-rules"]["security-rule-info"]["root"]["exports-hostname-info"]["name"] = security_rules
rule["exports-rule-info-2"]["security-rules"]["security-rule-info"]["sec-flavor"]["sec-flavor-info"]["flavor"] = 'sec'
Really though, put some effort into your questions...

Related

(STUPIDITY WARNING) Python - nested dictionary creation (and code cleanup?) [duplicate]

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.

How to use defaultdict in dict.fromkeys?

I want to count the histogram for a property value(depth here) of 3 different samples with 1 dictionary.
SamplesList = ('Sa','Sb','Sc')
from collections import defaultdict
DepthCnt = dict.fromkeys(SamplesList, defaultdict(int))
This code will make DepthCnt contains 3 defaultdict(int) of the same one, thus I cannot count different samples.
How can I do it right ?
It is OK to use either DepthCnt[sample][depth] or DepthCnt[depth][sample].
I tested these 3 ways:
from collections import defaultdict
DepthCnt = {key:defaultdict(int) for key in SamplesList}
yDepthCnt = defaultdict(lambda: defaultdict(int))
from collections import Counter
cDepthCnt = {key:Counter() for key in SamplesList}
The memory size are:
DepthCnt[sample][depth]: 993487
yDepthCnt[depth][sample]: 1953307
cDepthCnt[sample][depth]: 994207
It seems good to change to Counter().
Use a dictionary expression/comprehension/display
DepthCnt = {key:defaultdict(int) for key in SamplesList}
It sounds like you're trying to count occurences of sammples in SamplesList. If so you're looking for a collections.Counter
Given:
SamplesList = ('Sa','Sb','Sc')
Counter:
from collections import Counter
DepthCnt = Counter(SamplesList)
print(DepthCnt)
#Counter({'Sc': 1, 'Sa': 1, 'Sb': 1})
Edit:
You can always use a counter instead of a defaultdict as well
DepthCnt = {key:Counter() for key in SamplesList}
print(DepthCnt)
#DepthCnt = {'Sa': Counter(), 'Sb': Counter(), 'Sc': Counter()}
P.S
If you're working over a large dataset as well take a look into the Counter class both Counter and defaultdict are similar below is the TLDR from this great answer to a question on Collections.Counter vs defaultdict(int)
Counter supports most of the operations you can do on a multiset. So,
if you want to use those operation then go for Counter.
Counter won't add new keys to the dict when you query for missing
keys. So, if your queries include keys that may not be present in the
dict then better use Counter.
Counter also has a method called most_common that allows you to sort items by their count. To get the same thing in defaultdict you'll have to use sorted.

Cannot access sub-keys in OrderedDict

I'm trying to define my ordinary dictionary as an OrderedDict, but I can't seem to access keys are the inner level.
my_dict = \
{
'key1':
{
'subkey1':value1,
'subkey2':value2
}
}
my_ordered_dict = OrderedDict\
([(
'key1',
(
('subkey1',value1),
('subkey2',value2)
)
)])
I can access ['key1'] for both cases, but I cannot access ['key1']['subkey1'] for the ordered dict.
What am I doing wrong?
The dictionary with 'subkey1' should also be defined as a OrderedDict ,if thats what you want.
So it should be something like this
import collections
my_ordered_dict = collections.OrderedDict()
sub_dict=collections.OrderedDict()
sub_dict['subkey1']=1
sub_dict['subkey2']=2
my_ordered_dict['key1']=sub_dict
sub_dict=collections.OrderedDict()
sub_dict['subkey1']=3
sub_dict['subkey2']=4
my_ordered_dict['key2']=sub_dict
print my_ordered_dict['key1']['subkey1']
print my_ordered_dict['key2']['subkey1']
The out put will be
1
3
If you want the inner level to also be an OrderedDict you need to explicitly define it as one. Since argument order matters when constructing OrderedDicts, in order to preserve it, you need to pass the keys and values as a sequence of key, value pairs as shown in the OrderedDict Examples and Recipes section of the documentation.
Applying that to your example means you would need to do something like the following:
from collections import OrderedDict
value1, value2 = 42, 69
my_ordered_dict = OrderedDict([
('key1', OrderedDict([
('subkey1', value1),
('subkey2', value2)
])
)
])
print(my_ordered_dict['key1']['subkey1']) # --> 42

dynamic manipulation via dictionary keys

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

dynamically create the defaultdict(list) inside another defaultdict(tree)

I've been fighting with this for quite a long time and need some help, i have some loops searching something and dynamically create dictionary object for them, for example i am scanning a store then all the buckets and then the fruits inside those buckets.
from collections import defaultdict
def tree(): return defaultdict(tree)
test = tree()
test[Store][bucket][fruits] = defaultdict(list)
test[Store][bucket][fruits].append(1)
print test
"""
desired output
{
'Store':{
'bucket1':{
'fruits':['banana', 'mango', 'apple']
}
},
'bucket2':{
'fruits':['banana', 'mango', 'apple']
}
}
}
"""
this approach throws an error and doesn't work, i also tried many other approaches like creating different dictionaries and then merging them, or creating list and then search or find and blah blah.. but i would like to find out how can i merge defaultdict objects inside each other.
can someone please help me with this.
Thanks.
Given your desired output, I don't think you need to use defaultdict(list) at all.
Rather, you can use an ordinary list:
from collections import defaultdict
def tree():
return defaultdict(tree)
test = tree()
test['Store']['bucket']['fruits'] = []
test['Store']['bucket']['fruits'].append(1)
test['Store']['bucket-13']['fruits'] = ['mango', 'apple', 'banana']
print test
However, if you do want to use defaultdict(list), the reason why it's throwing an error is because it is one level too low in your heirarchy. You want to assign it at the "bucket" level, rather then the "fruits" level:
test = tree()
test['Store']['bucket'] = defaultdict(list)
test['Store']['bucket']['fruit'].append('mango')
test['Store']['bucket']['meat'].append('chicken')
# 'fruit' and 'meat' now default to an empty list

Categories