semi-extrinsic
semi-extrinsic

Reputation: 123

Python convert a list of nested tuples into a dict

Ok, so I am trying to write a Python function that turns the first line here, a list of nested tuples, into the second line, a flattened dictionary:

[('Ka',0.6), ('La', 0.6), (('Ma', 0.7), ('Na', 0.8), ('Oa', 0.9))]   
{'La': 0.6, 'Ma': 0.7, 'Ka': 0.6, 'Na': 0.8, 'Oa': 0.9}

A slight complication is that the outer tuples in the list are members of different objects, and that the argument of the function is a list of these objects. Hopefully the code below explains this.

Now I have actually managed to cobble together a solution to this problem, but it is so hideous that I have to ask how to do this in a more pythonic / less obfuscated way:

def theFunction(args):
    # Please don't ask me how this works. It just does.
    flatten = lambda *n: (e for a in n for e in (flatten(*a) if
        isinstance(a, (tuple, list)) else (a,)))
    return dict(list(zip(*([iter(list(flatten(list(l.sn for l in args))))]*2))))

class le:
    def __init__(self,n):
        self.sn = (n,0.6)

class lf:
    def __init__(self,n,m,o):
        self.sn = (n,0.7), (m,0.8), (o, 0.9)

l1 = le("Ka")
l2 = le("La")
l3 = lf("Ma","Na","Oa")
theList = [l1,l2,l3]
print([l.sn for l in theList])
print(theFunction(theList))

The two print statements produce as output the two lines at the top of the question.

Upvotes: 2

Views: 1180

Answers (2)

Adam Smith
Adam Smith

Reputation: 54213

You can easily write a recursive flattener, but this won't behave as expected for two-element tuples of tuples e.g. (('tuple','one'), ('tuple','two'))

def recurse_flatten(seq):
    for el in seq:
        if isinstance(el, tuple) and len(el)==2:
            yield el
        else:
            yield from recurse_flatten(el)

>>> dict(recurse_flatten([('Ka',0.6), ('La', 0.6), (('Ma', 0.7), ('Na', 0.8), ('Oa', 0.9))]))
{'Ma': 0.7, 'Na': 0.8, 'La': 0.6, 'Ka': 0.6, 'Oa': 0.9}

You might be able to have more success by refining your yield conditional a bit:

def recurse_flatten(seq):
    for el in seq:
        if isinstance(el, tuple) and len(el)==2:
            one,two = el
            if isinstance(one, str) and isinstance(two,float):
                yield el
                continue
        yield from recurse_flatten(el)

Upvotes: 0

senshin
senshin

Reputation: 10360

Can you change the definition of le so that self.sn is a tuple of tuples there, like it is in lf? If so, this is easy:

class le:
    def __init__(self, n):
        self.sn = (n, 0.6), 
        #                 ^ make self.sn a tuple of tuples in all cases

class lf:
    def __init__(self, n, m, o):
        self.sn = (n, 0.7), (m, 0.8), (o, 0.9)

l1 = le("Ka")
l2 = le("La")
l3 = lf("Ma","Na","Oa")
theList = [l1, l2, l3]
result = dict([tup for thing in theList for tup in thing.sn])
# result == {'Na': 0.8, 'Ka': 0.6, 'Ma': 0.7, 'Oa': 0.9, 'La': 0.6}

Also, maybe consider not using lowercase "L"s so liberally in short variable names, because it's a bear and a half to read in most fonts.

Upvotes: 2

Related Questions