How can I create a nested dict from a dict by dividing the keys on a python delimiter?

advertisements

I have a dict like the following:

a = {
  'customer_name': 'bob',
  'customer_phone': '555-1212',
  'order_0_number': 'A33432-24',
  'order_0_date': '12/12/12',
  'order_1_number': 'asd24222',
  'order_1_date': '12/14/12'
}

and I need it split on the underscore and put into a nested dict like this:

b = {
  'customer': {
    'name': 'bob',
    'phone': '555-1212'
  },
  'order': {
    '0': {
      'date': '12/12/12',
      'number': '...' },
    '1': { ... etc.

The actual data I have is more deeply nested than this. I'm this far into it, but getting stuck on how to do it in Python:

def expand_data(field, value):

  split_field = field.split('_', 1)

  #base case, end of string
  if len(split_field) == 1:
    child_element[split_field[0] = value
    return child_element
  else:
    child_element[split_field[0]] = expand_data(split_field[1],value)
    return child_element

b = {}
for k,v in a.iteritems():
  b += expand_data(k, v) # stuck here because I can't add nested dicts together

but I'm not entirely sure this is even the right way to go about it. I haven't run this code yet, just trying to think about it right now.

Also, the dict keys may change in the future so all I can rely on is the '_' underscore being there to split on. I also won't know how deeply nested it's going to need to be.


A generic solution:

def nest_dict(flat_dict, sep='_'):
    """Return nested dict by splitting the keys on a delimiter.

    >>> from pprint import pprint
    >>> pprint(nest_dict({'title': 'foo', 'author_name': 'stretch',
    ... 'author_zipcode': '06901'}))
    {'author': {'name': 'stretch', 'zipcode': '06901'}, 'title': 'foo'}
    """
    tree = {}
    for key, val in flat_dict.items():
        t = tree
        prev = None
        for part in key.split(sep):
            if prev is not None:
                t = t.setdefault(prev, {})
            prev = part
        else:
            t.setdefault(prev, val)
    return tree

if __name__ == '__main__':
    import doctest
    doctest.testmod()