Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python List Comprehension: Using "if" Statement on Result of the Comprehension

Can you filter a list comprehension based on the result of the transformation in the comprehension?

For example, suppose you want to strip each string in a list, and remove strings that are just whitespace. I could easily do the following:

filter(None, [x.strip() for x in str_list])

But that iterates over the list twice. Alternatively you could do the following:

[x.strip() for x in str_list if x.strip()]

But that implementation performs strip twice. I could also use a generator:

for x in str_list:
   x = x.strip()
   if x:
      yield x

But now that's a bunch of lines of code. Is there any way of doing the above (1) only iterating once; (2) only performing the transformation once; and (3) in a single list comprehension? The example above is a toy example, but I'd like to do this with longer lists and non-trivial transforms.

Update: I'm using Python 2.7.X, and prefer answers in that, but if Python 3 has some new features that make this easy, I'd be happy to learn about them as well.

like image 791
speedplane Avatar asked Nov 21 '17 03:11

speedplane


1 Answers

Don't pass a list to filter, pass a generator expression, and it will only be iterated once:

filter(None, (x.strip() for x in str_list))

This is exactly the same idea as using a nested generator like

[y for y in (x.strip() for x in str_list) if y]

Both cases rely on the lazy evaluation of generators: each element of str_list will be processed exactly once, when the corresponding output element is created. No intermediate lists will be made.

The comprehension approach is nice for small one-shot transformations like these. Even the simple example here, which filters after the transformation, is pushing the limits of readability in my opinion. With any non-trivial sequence of transformations and filters, I would recommend using a for loop.

like image 180
Mad Physicist Avatar answered Sep 28 '22 05:09

Mad Physicist