Multiple levels of 'collection.defaultdict' in Python
Use:
from collections import defaultdict
d = defaultdict(lambda: defaultdict(int))
This will create a new defaultdict(int)
whenever a new key is accessed in d
.
Multi-level defaultdict with variable depth?
You can do it without even defining a class:
from collections import defaultdict
nested_dict = lambda: defaultdict(nested_dict)
nest = nested_dict()
nest[0][1][2][3][4][5] = 6
defaultdict of defaultdict?
Yes like this:
defaultdict(lambda: defaultdict(int))
The argument of a defaultdict
(in this case is lambda: defaultdict(int)
) will be called when you try to access a key that doesn't exist. The return value of it will be set as the new value of this key, which means in our case the value of d[Key_doesnt_exist]
will be defaultdict(int)
.
If you try to access a key from this last defaultdict i.e. d[Key_doesnt_exist][Key_doesnt_exist]
it will return 0, which is the return value of the argument of the last defaultdict i.e. int()
.
Nested defaultdict of defaultdict
For an arbitrary number of levels:
def rec_dd():
return defaultdict(rec_dd)
>>> x = rec_dd()
>>> x['a']['b']['c']['d']
defaultdict(<function rec_dd at 0x7f0dcef81500>, {})
>>> print json.dumps(x)
{"a": {"b": {"c": {"d": {}}}}}
Of course you could also do this with a lambda, but I find lambdas to be less readable. In any case it would look like this:
rec_dd = lambda: defaultdict(rec_dd)
Python multi level default dict
Your nested defaultdict
makes nestedDict[...][...]
be a list
, but then you assign a string to it. I don't think you need that assignment anyway: why not just let the loop handle all of the positions?
Python: defaultdict(dict) how to create nested dictionary?
A dictionary is something that has a key and a value, any time you put something into a dictionary that has the same key, it will replace that value. For instance:
dictionary = {}
# Insert value1 at index key1
dictionary["key1"] = "value1"
# Overwrite the value at index key1 to value2
dictionary["key1"] = "value2"
print(dictionary["key1"]) # prints value2
There are a few things that don't make sense about your code, but I suggest you use the above syntax for actually adding things to the dictionary (rather than the update method as that is used for adding a list of key/value pairs into a dictionary).
Below I marked up some suggestions to your code with #**'s
import collections
import os
#** I'm guessing you want to put your devices into this dict, but you never put anything into it
devices = collections.defaultdict(lambda: collections.defaultdict(dict))
# bundles = collections.defaultdict(dict)
for filename in os.listdir('.'):
if os.path.isfile(filename):
if '.net' in filename:
dev = open(filename)
for line in dev:
line = line.strip()
values = line.split(' ')
#** declare bundle_name out here, it is better form since we will make use of it in the immediate scopes below this
bundle_name = ''
if values[0] == 'interface':
bundle_name = values[1]
if '/' in bundle_name:
pass
else:
if len(values) == 2:
#** This is assuming you populated devices somewhere else??
devices[filename][bundle_name] = []
if 'secondary' in values:
pass
elif values[0] == 'ipv4' and values[1] == 'address' and len(values) == 4:
#** ur_device was not declared in this scope, it is a bad idea to use it here, instead index it out of the devices dict
#** it also might be worth having a check here to ensure devices[filename] actually exists first
devices[filename][bundle_name].append(
{
'ip_address': values[2],
'subnet_mask': values[3],
}
)
dev.close()
** Edit **
Looking at your question, you need to have multiple bundles for each filename. But your dictionary structure is not sufficiently detailed and you don't seem to provide the code for how you initialized devices with data.
Basically, you should think about what keys each level of the dictionary is going to have, not just what values.
devices (filename: device)
device (bundle_name?: info) <-- You are missing this dictionary
info (list that you append details to)
I changed the above code with my best guess as to what you intended.
How to default a nested defaultdict() to a list of specified length?
You can specify that you want a default value of a defaultdict
that has a default value of [0, 0, 0]
from collections import defaultdict
dic01 = defaultdict(lambda: defaultdict(lambda: [0, 0, 0]))
dic01['year2018']['jul_to_sep_temperature'][0] = 25
print(dic01)
prints
defaultdict(<function <lambda> at 0x7f4dc28ac598>, {'year2018': defaultdict(<function <lambda>.<locals>.<lambda> at 0x7f4dc28ac510>, {'jul_to_sep_temperature': [25, 0, 0]})})
Which you can treat as a regular nested dictionary
rebuilding arrays with nested defaultdict
Using groupby
as suggested by @Pynchia and using sorted
for unordered data as suggested by @hege_hegedus:
from itertools import groupby
dataOut = []
dataSorted = sorted(data, key=lambda x: (x["CustName"], x["PartNum"]))
for cust_name, cust_group in groupby(dataSorted, lambda x: x["CustName"]):
dataOut.append({
"CustName": cust_name,
"Parts": [],
})
for part_num, part_group in groupby(cust_group, lambda x: x["PartNum"]):
dataOut[-1]["Parts"].append({
"PartNum": part_num,
"deliveries": [{
"delKey": delivery["delKey"],
"memo": delivery["memo"],
"qty": delivery["qty"],
} for delivery in part_group]
})
If you look at the second for
loop, this will hopefully answer your question about accessing the second level array in the loop.
Related Topics
String to Dictionary in Python
How to Save and Load Numpy.Array() Data Properly
Add Params to Given Url in Python
Python Convert Tuple to String
Implementation Hmac-Sha1 in Python
Moving X-Axis to the Top of a Plot in Matplotlib
How to Encrypt and Decrypt a String in Python
If Range() Is a Generator in Python 3.3, Why How to Not Call Next() on a Range
Use .Corr to Get the Correlation Between Two Columns
Getting the SQL from a Django Queryset
Python: Load Variables in a Dict into Namespace
Round Number to Nearest Integer
Debugging (Displaying) SQL Command Sent to the Db by SQLalchemy
Splitting a Semicolon-Separated String to a Dictionary, in Python
Use Pytesseract Ocr to Recognize Text from an Image
Named Regular Expression Group "(P<Group_Name>Regexp)": What Does "P" Stand For