Jeet
Jeet

Reputation: 11

How do I make else return nothing in a nested statement in list comprehension

I want the else statement in my list comprehension return nothing - i.e. I want the output of only the 2 if statements in my code. How can I do this please?

Data:

pleasant_sounding = ['Farm', 'Park', 'Hill', 'Green']

royal_sounding = ['Royal', 'Queen', 'King']

location_dict['Outer London'] = ['Brent Park',  'Woodford', 'Dollis Hill', 'Park Royal',  'Redbridge']

My code:

    [ '{} sounds pleasant'.format(name) 
if any(x in name for x in pleasant_sounding)
else '{} sounds grand'.format(name) 
if any(y in name for y in royal_sounding)
else '' for name in location_dict['Outer London'] ]

My Output:

Brent Park sounds pleasant

''

Dollis Hill sounds pleasant

Park Royal sounds grand

''

Expected Output:

Brent Park sounds pleasant

Dollis Hill sounds pleasant

Park Royal sounds grand

Upvotes: 1

Views: 622

Answers (2)

jferard
jferard

Reputation: 8180

Your list comprehension returns

['Brent Park sounds pleasant', '', 'Dollis Hill sounds pleasant', 'Park Royal sounds pleasant', '']

You just have to filter it:

>>> [t for t in <your list comprehension here> if t != '' ]
['Brent Park sounds pleasant', 'Dollis Hill sounds pleasant', 'Park Royal sounds pleasant']

That is:

>>> [t for t in ('{} sounds pleasant'.format(name)
... if any(x in name for x in pleasant_sounding)
... else '{} sounds grand'.format(name)
... if any(y in name for y in royal_sounding)
... else '' for name in location_dict['Outer London']) if t != '' ]
['Brent Park sounds pleasant', 'Dollis Hill sounds pleasant', 'Park Royal sounds pleasant']

I used a generator (note the parentheses) for the inner part since we don't need to construct the list but just to evaluate the values one by one. The code is still unclear, because you have, in the middle of the list comprehension, a complex expression that creates the string to return. You should use a function:

>>> def text(name):
...     if any(x in name for x in pleasant_sounding):
...         return '{} sounds pleasant'.format(name)
...     elif any(y in name for y in royal_sounding):
...         return '{} sounds grand'.format(name)
...     return None # None is better than '' here
... 
>>> [t for t in (text(name) for name in location_dict['Outer London']) if t is not None ]
['Brent Park sounds pleasant', 'Dollis Hill sounds pleasant', 'Park Royal sounds pleasant']

You can use a more functional style if you want:

>>> list(filter(None, map(text, location_dict['Outer London'])))
['Brent Park sounds pleasant', 'Dollis Hill sounds pleasant', 'Park Royal sounds pleasant']

I still see some redundancy in your if any(name ...) tests. Imagine you have a lot of sounding types: your code will become tedious to maintain. You may use a more general method:

>>> soundings = [("pleasant", ['Farm', 'Park', 'Hill', 'Green']), ("grand", ['Royal', 'Queen', 'King'])}
>>> def text(name):
...     for sounding_type, substrings in soundings:
...         if any(x in name for x in substrings):
...             return '{} sounds {}'.format(name, sounding_type)
...     return None
... 
>>> [t for t in (text(name) for name in location_dict['Outer London']) if t is not None]
['Brent Park sounds pleasant', 'Dollis Hill sounds pleasant', 'Park Royal sounds pleasant']

Note: this is Python 3.7, but you can adapt it to Python 2.7 (iteritems instead of items).

Upvotes: 1

6502
6502

Reputation: 114521

You can also add an if in list comprehensions... your code could produce what you're looking for with some simplification and a little addition at the end:

['{} sounds pleasant'.format(name) if any(x in name for x in pleasant_sounding)
 else '{} sounds grand'.format(name)
 for name in location_dict['Outer London']
 if any(x in name for x in pleasant_sounding+royal_sounding)]

In other words just a ternary expression, with a comprehension that includes a filtering condition

[<X> if <condition> else <Y>
 for <var> in <container>
 if <test>]

Upvotes: 1

Related Questions