0

I need to generate a dictionary like this:

{
  'newEnv': {
     'newProj': {
        'newComp': {
           'instances': [],
           'n_thing': 'newThing'
        }
     }
  }
}

from a tuple, like this: ('newEnv','newProj','newComp','newThing') but only if that doesn't already exists. So, I tried this:

myDict = {}
(env,proj,comp,thing) = ('newEnv','newProj','newComp','newThing')

if env not in myDict:
    myDict[env] = {}
if proj not in myDict[env]:
    myDict[env][proj] = {}
if comp not in myDict[env][proj]:
    myDict[env][proj][comp] = {'n_thing': thing, 'instances': []}

which is pretty much working but not sure how efficient is that or if I should be doing this way at all. Any suggestion(s)??

1
  • By the way, your solution is not bad by any means. Commented Jan 9, 2014 at 16:01

2 Answers 2

6

You can use a loop (with just the first 3 keys, newThing is not a key in the chain):

myDict = {}
path = ('newEnv','newProj','newComp')
current = myDict
for key in path:
    current = current.setdefault(key, {})

where current ends up as the innermost dictionary, letting you set the 'n_thing' and 'instances' keys on that.

You could use reduce() to collapse that into a one-liner:

myDict = {}
path = ('newEnv','newProj','newComp')
reduce(lambda d, k: d.setdefault(k, {}), path, myDict)

The reduce call returns the innermost dictionary, so you can use that to assign your final value:

myDict = {}
path = ('newEnv','newProj','newComp')
inner = reduce(lambda d, k: d.setdefault(k, {}), path, myDict)
inner.update({'n_thing': 'newThing', 'instances': []})
Sign up to request clarification or add additional context in comments.

4 Comments

current = myDict.setdefault(key, {}) should be current = current.setdefault(key, {}
@MartijnPieters: that's really cool! worked just fine in the console, let me try in my original script.
@MartijnPieters: there is a slight problem implementing this in my script. I run this inside a for loop: for queue in get_all_queues: and for the 'instances': [] bit, I do this: myDict[env][proj][comp]['instances'].append(queue) to populate the 'instances' list after the last if statement. With new script, it's not being appended to the list but having only the last one. Looks like it's because of inner.update()? How to I fix that? Cheers!!
Yes, the update would replace the key; use inner.setdefault('instances', []).append(queue) (and set the other key with straight assignment).
0

you can do something similar, maybe slightly simpler, with a defaultdict of defaultdicts (see defaultdict of defaultdict, nested for some discussion)

tree = lambda: defaultdict(tree)

base = tree()
for x in mytuple[:-2]:
    base = base[x]
base[x] = mytuple[-1]

which is very similar to martijn's, just using the default functionality to create the subdicts instead of doing it directly with setdefault.

this also lets you type directly

myDict[env][proj][comp].setdefault('instances', list()).append(queue)

if that's what you are really looking for. (there's no way to remove the setdefault unfortunately; after all, I don't know ahead of time whether you want a list or dict. you only have one default)....

1 Comment

That's a nice solution too but I've accepted martijn's as it's going very well with the rest of the script. thanks for showing the different route. Cheers!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.