Search code examples
pythonlist-comprehensioniterable-unpacking

how can unpack a list in list comprehension


I have a list of dict which each dict can have a list of dict nested in like:

mydictlist = [{'name':'foo'}, {'name':'bar','next-level':[{'name':'next-level-foo'}, {'name':'next-level-bar'}] } ]

I am trying to flatten some kind of attribute like name in list comprehension like this:

flattened = [ *['{}_{}'.format(iter['name'],next['name']) for next in iter] if 'next-level' in iter else '{}'.format(iter['name']) for iter in mydictlist]

to get something like :

['foo', 'bar_next-level-foo', 'bar_next-level-bar']

but this result in error!. I can do it with for and if without list comprehension (and have done that) but I want to know what is the correct syntax for doing that with list (or tuple unpacking) and list comprehension?


Solution

  • The closest one I can do is to provide a "fake" second level when necessary, and ignoring it afterwards:

    mydictlist = [{'name':'foo'}, {'name':'bar','next-level':[{'name':'next-level-foo'}, {'name':'next-level-bar'}] } ]
    
    flattened = [ '{}_{}'.format(item['name'],next['name']) if next['name'] else item['name'] for item in mydictlist for next in (item['next-level'] if 'next-level' in item else  [{'name':None}]) ]
    

    Output:

    ['foo', 'bar_next-level-foo', 'bar_next-level-bar']
    

    There do not seem a way for conditionally loop in a list comprehension. So if there are two for-s, they will both run, but the "subject" of the second loop can be generated conditionally (originally I did that with a lambda, but apparently a parenthesed expression is enough).

    Your attempt had a bug, for next in iter should have been for next in iter['next-level'] (also, iter() is a built-in function, so I renamed the thing to item).
    And if you tried something less ambitious, you would have encountered an explicit error message: iterable unpacking cannot be used in comprehension.