list comprehension without if but with else

Viewed 106

My question aims to use the else condition of a for-loop in a list comprehension.

example:

empty_list = []
def example_func(text):
    for a in text.split():
        for b in a.split(","):
            empty_list.append(b)
        else:
            empty_list.append(" ")

I would like to make it cleaner by using a list comprehension with both for-loops.

But how can I do this by including an escape-clause for one of the loops (in this case the 2nd). I know I can use if with and without else in a list comprehension. But how about using else without an if statement.

Is there a way, so the interpreter will understand it as escape-clause of a for loop?

Any help is much appreciated!

EDIT: Thanks for the answers! In fact im trying to translate morse code.

The input is a string, containing morse codes. Each word is separated by 3 spaces. Each letter of each word is separated by 1 space.

def decoder(code): 
    str_list = [] 
    for i in code.split("   "): 
        for e in i.split(): 
            str_list.append(morse_code_dic[e]) 
        else: 
            str_list.append(" ") 
     return "".join(str_list[:-1]).capitalize()

print(decoder(".. -   .-- .- ...   .-   --. --- --- -..   -.. .- -.--"))

I want to break down the whole sentence into words, then translate each word. After the inner loop (translation of one word) is finished, it will launch its escape-clause else, adding a space, so that the structure of the whole sentence will be preserved. That way, the 3 Spaces will be translated to one space.

2 Answers

As noted in comments, that else does not really make all that much sense, since the purpose of an else after a for loop is actually to hold code for conditional execution if the loop terminates normally (i.e. not via break), which your loop always does, thus it is always executed.

So this is not really an answer to the question how to do that in a list comprehension, but more of an alternative. Instead of adding spaces after all words, then removing the last space and joining everything together, you could just use two nested join generator expressions, one for the sentence and one for the words:

def decoder(code): 
    return " ".join("".join(morse_code_dic[e] for e in i.split())
                    for i in code.split("   ")).capitalize()

As mentioned in the comments, the else clause in your particular example is pointless because it always runs. Let's contrive an example that would let us investigate the possibility of simulating a break and else.

Take the following string:

s = 'a,b,c b,c,d c,d,e, d,e,f'

Let's say you wanted to split the string by spaces and commas as before, but you only wanted to preserve the elements of the inner split up to the first occurrence of c:

out = []
for i in s.split(): 
    for e in i.split(','):
        if e == 'c':
            break
        out.append(e)
    else: 
        out.append('-')

The break can be simulated using the arcane two-arg form of iter, which accepts a callable and a termination value:

>>> x = list('abcd')
>>> list(iter(iter(x).__next__, 'c'))
['a', 'b']

You can implement the else by chaining the inner iterable with ['-'].

>>> from itertools import chain
>>> x = list('abcd')
>>> list(iter(chain(x, ['-'])
.__next__, 'c'))
['a', 'b']
>>> y = list('def')
>>> list(iter(chain(y, ['-'])
.__next__, 'c'))
['d', 'e', 'f', '-']

Notice that the placement of chain is crucial here. If you were to chain the dash to the outer iterator, it would always be appended, not only when c is not encountered:

>>> list(chain(iter(iter(x).__next__, 'c'), ['-']))
['a', 'b', '-']

You can now simulate the entire nested loop with a single expression:

from itertools import chain

out = [e for i in s.split() for e in iter(chain(i.split(','), ['-']).__next__, 'c')]
Related