29

I'm currently using the method below to define a multidimensional dictionary in python. My question is: Is this the preferred way of defining multidimensional dicts?

from collections import defaultdict

def site_struct(): 
    return defaultdict(board_struct)

def board_struct(): 
    return defaultdict(user_struct)

def user_struct(): 
    return dict(pageviews=0,username='',comments=0)

userdict = defaultdict(site_struct)

to get the following structure:

userdict['site1']['board1']['username'] = 'tommy'

I'm also using this to increment counters on the fly for a user without having to check if a key exists or is set to 0 already. E.g.:

userdict['site1']['board1']['username']['pageviews'] += 1
Aaron Hall
  • 291,450
  • 75
  • 369
  • 312
James
  • 13,765
  • 22
  • 80
  • 116

4 Answers4

45

Tuples are hashable. Probably I'm missing the point, but why don't you use a standard dictionary with the convention that the keys will be triples? For example:

userdict = {}
userdict[('site1', 'board1', 'username')] = 'tommy'
Federico A. Ramponi
  • 43,319
  • 29
  • 102
  • 130
16

You can create a multidimensional dictionary of any type like this:

from collections import defaultdict
from collections import Counter

def multi_dimensions(n, type):
  """ Creates an n-dimension dictionary where the n-th dimension is of type 'type'
  """  
  if n<=1:
    return type()
  return defaultdict(lambda:multi_dimensions(n-1, type))

>>> m = multi_dimensions(5, Counter)
>>> m['d1']['d2']['d3']['d4']
Counter()
andersonvom
  • 11,249
  • 4
  • 33
  • 37
  • When I try this, I get `TypeError: Required argument 'name' (pos 1) not found` which refers to the line `return type()`. – hepcat72 May 23 '16 at 21:47
  • I figured out my issue - it's just a difference between how I define "N-Dimensional" dictionaries & how it's defined above. E.g. a single datum is indexed by 0 dimensions. A simple array is indexed by 1 dim. The vals in an array of arrays is indexed by 2 dims. And dicts are just arrays indexed by strings. The vals it holds are not (to me) an additional dimension, though I see how you could think of it that way. In the example above, I would've called it a 4D dict, not 5. So my code wasn't working because I was expecting the type instead of a dict @ 1 dimension shy of what it gave me. – hepcat72 May 24 '16 at 13:38
1

This is highly subjective - but if your dict is getting to be more than 2-n deep, I would revert to creating a class and defining methods/properties to do what you want.

I think your end goal would be to create a "multi-column" data structure so you can store any number of attributes for a particular object (in this case, a site). Creating a site class would accomplish that and then you can stick instances wherever you would any other object (variable) - with some noted exceptions.

Burhan Khalid
  • 152,028
  • 17
  • 215
  • 255
1

This is a pretty subjective question from my perspective. For me, the real question would be at what point do you promote this nested data structure to objects with methods to insulate you from changes. However, I've been known to create large prototyping namespaces with the following:

from collections import defaultdict

def nesteddict(): 
  return defaultdict(nesteddict)
Shane Holloway
  • 6,469
  • 4
  • 26
  • 36
  • This solution preserves the ability to iterate over dimensions, but does create a dependency for nesteddict. – GregB Aug 29 '11 at 23:58