3

Possible Duplicate:
python: Dictionaries of dictionaries merge

I'm keeping a bunch of app settings stored in a dictionary that is saved to a text file as JSON. I'd like to to merge it with a default set of key/values, so that as the app changes in the future, the new ones can be merged in. For example:

defaults = { 
       'svn': "", 
       'notify': { 
          'email': "", 
          'notifo': { 'username': "", 'secret': ""},
          'active': False,
          'lastCheck': 0
       }
    }

local =  { 
       'svn': "/path/to/trunk/", 
       'notify': { 
          'email': "me@mysite.com", 
          'notifo': { 'username': "me", 'secret': "1234"},
       }
    }

Notice that 'local' is missing ['notify']['active'] and ['notify']['lastCheck']. I want to be able to merge the two dictionaries into one that looks like this:

local =  { 
       'svn': "/path/to/trunk/", 
       'notify': { 
          'email': "me@mysite.com", 
          'notifo': { 'username': "me", 'secret': "1234"},
          'active': False,
          'lastCheck': 0
       }
    }

I've been looking everywhere but only see examples of people flattening the dictionaries, merging the first level or doing funny things with defaultdict. Is it possible to recursively merge nested dictionaries.

Community
  • 1
  • 1
Jeremy Gillick
  • 2,560
  • 3
  • 27
  • 35

1 Answers1

4

I think the following function might do what you want, but I've not checked all possible combinations of missing keys in each dictionary. Also, this is probably not optimized for large dicts:

import types 
def merge(x,y):
    # store a copy of x, but overwrite with y's values where applicable         
    merged = dict(x,**y)

    xkeys = x.keys()

    # if the value of merged[key] was overwritten with y[key]'s value           
    # then we need to put back any missing x[key] values                        
    for key in xkeys:
        # if this key is a dictionary, recurse                                  
        if type(x[key]) is types.DictType and y.has_key(key):
            merged[key] = merge(x[key],y[key])

    return merged

It appears to work on your example, plus a few extra items I added for complexity, by putting the merge function and the following bit in a file test.py:

defaults = {
       'svn': "",
       'notify': {
          'email': "",
          'notifo': { 'username': "", 'secret': ""},
          'active': False,
          'lastCheck': 0
       },
       'test': "another test"
    }

local =  {
       'svn': "/path/to/trunk/",
       'notify': {
          'email': "me@mysite.com",
          'notifo': { 'username': "me", 'secret': "1234", 'notx': 39},
       },
       'test2': 'again a test'
    }

print merge(defaults,local)

and running gives:

$ python test.py
{'svn': '/path/to/trunk/', 'test2': 'again a test', 'notify': {'lastCheck': 0, 'notifo': {'username': 'me', 'secret': '1234', 'notx': 39}, 'active': False, 'email': 'me@mysite.com'}, 'test': 'another test'}
cm2
  • 1,815
  • 15
  • 20