Multiple Levels of 'Collection.Defaultdict' in Python

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



Leave a reply



Submit