Reputation: 82
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
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
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
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
Reputation: 170269
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