How to make a nested dictionary and dynamically append data

12,089

Solution 1

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)

Solution 2

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)

Solution 3

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)

Solution 4

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)

Share:
12,089

Related videos on Youtube

nookie
Author by

nookie

Updated on June 04, 2022

Comments

  • nookie
    nookie about 2 years

    I have a loop giving me three variables

    matteGroup
    matteName
    object
    

    I would like to make a nested dicionary holding all the data like:

    dictionary{matteGroup: {matteName: obj1, obj2, ob3} }
    

    I am checking the objects one by one so I would like to create the matteGroup if it doesn't exist, create the matteName if it doesn't exixst and then create or append the name of the object. I tryed a lot of solution like normal dictionaries, defaultdict and some custom classes I found on the net, but I haven't been able to do it properly. I have a nice nesting I am not able to append, or vice versa.

    This is the loop

        dizGroup = {}
        dizName = {}
    
        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] = list()
                dizGroup[matteGroup].append(matteName)
    
                if matteName not in dizName:
                    dizName[matteName] = list()
                dizName[matteName].append(obj)
    

    with this I get the two dictionaries separately, but is not so useful! Any hint?

    Thanks

  • silent_sight
    silent_sight about 7 years
    you could also combine the last two try/except clauses and just try to append the obj to the list first... it does get a little more difficult though, since you won't know which dict doesn't contain the key you're trying to access, so you have to account for that with nested try/except clauses if you really want to... aaaaaaand I just realized this question is from almost 5 and a half years ago, so I doubt anyone will even look at this answer, heh.