I have a loop giving me three variables
matteGroup
matteName
object
I would like to make a nested dicionary holding all the data like:
Provided I've understood your requirements correctly:
In [25]: from collections import defaultdict
In [26]: d = defaultdict(lambda: defaultdict(list))
In [30]: for group, name, obj in [('g1','n1','o1'),('g1','n2','o2'),('g1','n1','o3'),('g2','n1','o4')]:
....: d[group][name].append(obj)
if speed is a concern, you could utilize try/except clauses to just try to populate your data first rather than checking if items exist and then adding it each time through the loop
diz = {}
for obj in mc.ls(type='transform'):
try:
matteGroup = mc.getAttr('%s.matteGroup' %obj)
matteName = mc.getAttr('%s.matteName' %obj)
except Exception:
continue
try:
diz[matteGroup]
except KeyError:
diz[matteGroup] = {matteName : [obj]}
continue
try:
diz[matteGroup][matteName].append(obj)
except KeyError:
diz[matteGroup][matteName] = [obj]
for the first try/except, it would be best to put whatever exception maya throws if an attr doesn't exist on a node (don't have maya open right now, so I couldn't put that in...). This essentially checks for the attr and continues to the next obj if the attr isn't there. You can put them both in there instead of each having their own try/except, because it should error if either doesn't exist anyway.
the second try/except is checking if the matteGroup is in the top level of your dict. If it isn't, then you know the matteName and list of obj's isn't in your data structure either, so it adds them and continues to the next obj
the third try/except tries to append the obj to the matteName dict item's list. If you get a keyError here, it means that the matteName isn't in your matteGroup dict, so it then adds it and creates the list with the current obj as the first item in that list.
So, as far as speed is concerned, any time items exist in your data structure, the next obj you add to that data item will essentially just get added without having to test if all of the other data structure is in place before adding it, making your loop go faster the further into the loop you go (provided there are a lot of nodes sharing matteGroups and/or matteNames)
try something like this
dizGroup = {}
for obj in mc.ls(type='transform'):
if mc.objExists(obj + ('.matteGroup')):
matteGroup = mc.getAttr(obj + ('.matteGroup'))
matteName = mc.getAttr(obj + ('.matteName'))
if matteGroup not in dizGroup:
dizGroup[matteGroup] = {}
if matteName not in dizGroup[matteGroup]:
dizGroup[matteGroup][matteName] = []
dizGroup[matteGroup][matteName].append(obj)
Due to an issue with pickling my object, that used some of the previous answers, I tried to solve this as well. This is what worked for me for dynamically adding new keys to two different sub levels of dictionaries:
from collections import defaultdict
test = defaultdict(defaultdict)
test["level1"]["level2"] = 1
test["level1"]["level2_second"] = 2
print(test)
defaultdict(, {'level1': defaultdict(None, {'level2': 1, 'level2_second': 2})})
This solution seems to be only able to handle two levels of nesting.
Look at the defaultdict
in the collections
module.
Here's a simple example that looks like what you're going for:
>>> from collections import defaultdict
>>> dizGroup = defaultdict(lambda:defaultdict(list))
>>> dizGroup['group1']['name1'].append(1)
>>> dizGroup['group1']['name1'].append(2)
>>> dizGroup['group1']['name1'].append(3)
>>> dizGroup['group1']['name2'].append(4)
>>> dizGroup['group1']['name2'].append(5)
>>> dizGroup['group2']['name1'].append(6)
>>> dizGroup
defaultdict(<function <lambda> at 0x7ffcb5ace9b0>, {'group1': defaultdict(<type 'list'>, {'name2': [4, 5], 'name1': [1, 2, 3]}), 'group2': defaultdict(<type 'list'>, {'name1': [6]})})
So, you should just need this:
if mc.objExists(obj + ('.matteGroup')):
matteGroup = mc.getAttr(obj + ('.matteGroup'))
matteName = mc.getAttr(obj + ('.matteName'))
dizGroup[matteGroup][matteName].append(obj)