katamayros
katamayros

Reputation: 82

Python: Convert a nested list of tuples of lists into a dictionary?

I have a nested list of tuples of lists (of tuples, etc.) that looks like this:

[(' person', 
[(('surname', u'Doe', True),), 
(('name', u'John', False),), 
('contact', 
[(('email', u'john@doe.me', True),), 
(('phone', u'+0123456789', False),), 
(('fax', u'+0987654321', False),)]), 
('connection', 
[(('company', u'ibcn', True),), 
('contact', 
[(('email', u'mail@ibcn.com', True),), 
(('address', u'main street 0', False),), 
(('city', u'pythonville', False),), 
(('fax', u'+0987654321', False),)])])])]

There is no way of knowing neither the number of (double) tuples within a list nor how deep nesting goes.

I want to convert it to a nested dictionary (of dictionaries), eliminating the boolean values, like this:

{'person': {'name': 'John', 'surname': 'Doe', 
    'contact': {'phone': '+0123456789', 'email': 'john@doe.me','fax': '+0987654321',
    'connection': {'company name': 'ibcn', 'contact':{'phone': '+2345678901',
                   'email': 'mail@ibcn.com', 'address': 'main street 0'
                   'city': 'pythonville', 'fax': +0987654321'
}}}}}

All I have, so far, is a recursive method that can print the nested structure in a per-line fashion:

def tuple2dict(_tuple):
    for item in _tuple:
        if type(item) == StringType or type(item) == UnicodeType:
            print item
        elif type(item) == BooleanType:
            pass
        else:
            tuple2dict(item)

but, I'm not sure I'm on the right track...

EDIT: I've edited the original structure, since it was missing a comma.

Upvotes: 1

Views: 3267

Answers (4)

katamayros
katamayros

Reputation: 82

This is my final solution. Not very elegant, I must admit. It also takes care of multiple entries for a key by concatenating it with the existing one.

def tuple2dict(_obj):
    _dict = {}
    for item in _obj:
        if isinstance(item, tuple) or isinstance(item, list):
            if isinstance(item[0], basestring):
                _dict[item[0]] = tuple2dict(item[1])
            else:
                if isinstance(item[0], tuple):
                    # if the key already exists, then concatenate the old
                    # value with the new one, adding a space in between.
                    _key = item[0][0]
                    if _key in _dict:
                        _dict[_key] = " ".join([_dict[_key], item[0][1]])
                    else:
                        _dict[_key] = item[0][1]
    return _dict

Upvotes: 0

Joran Beasley
Joran Beasley

Reputation: 114098

not beautiful ... but it works... basically

def t2d(t):
 if isinstance(t,basestring):return t
 length = len(t)
 if length == 1:
     return t2d(t[0])
 if length == 2:

     t1,t2 = t2d(t[0]),t2d(t[1])
     print "T:",t1,t2
     if isinstance(t1,dict) and len(t1) == 1:
         t2['name'] = t1.values()[0]
         t1 = t1.keys()[0]
     return dict([[t1,t2]])
 if length == 3 and isinstance(t[2],bool):
     return t2d(t[:2])

 L1 =[t2d(tp) for tp in t]
 L2 = [lx.items() for lx in L1]
 L3 = dict( [i[0] for i in L2])
 return L3

I should mention it works specifically with the dict you posted... (seems like company wasnt quite setup right so I hacked it (see t2['name']...))

Upvotes: 1

Hans Then
Hans Then

Reputation: 11322

You are on the right track. The recursive approach will work. As far as I can tell from your sample data, each tuple first has string item, containing the key. After that you have either another tuple or list as value, or a String value followed by a boolean true or false.

EDIT:

The trick with recursion is that you have to know when to stop. Basically, in your case it appears the deepest structure are the nested three tuples, matching names to values.

Hacking away a bit. I shamefully admit this is the ugliest code in the world.

def tuple2dict(data):
    d = {}
    for item in data:
        if len(item) == 1 and isinstance(item, tuple):
            # remove the nested structure, you may need a loop here
            item = item[0]
            key = item[0]
            value = item[1]
            d[key] = value
            continue
        key = item[0]
        value = item[1]
        if hasattr(value, '__getitem__'):
            value = tuple2dict(value)
        d[key] = value
    return d

Upvotes: 3

You are definitely on the right track. A recursive function is the way to go.

A dictionary can be built from an iterable giving tuples of length 2. Now, to get rid of the boolean, you can use slicing. Just slice off everything except the first two elements.

 >> (1,2,3)[:3]
 (1,2)

Upvotes: 0

Related Questions