Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

"If...or..." statement inside list comprehension?

In Python 3.4.2, is there a way to have an "if ... or ..." statement in a list comprehension?

Example:

filtered_list = [x for x in other_list
                 if not '    ' in x or '    ' in other_list[int(other_list.index(x) -1)]]
                                                 #^item just before "x" in "other_list"

I know this command doesn't work. I know this one doesn't either:

filtered_list = [x for x in other_list
                 if not ('    ' in x) or ('    ' in other_list[int(other_list.index(x) -1)])]
                                                    #^item just before "x" in "other_list"

Is there a way to do this? A "for" loop would be way easier to read, but I'm trying to push my understanding of list comprehensions. Thanks in advance.

(Oddly enough, I can't find a previous question like this on stackoverflow.)

like image 675
GreenRaccoon23 Avatar asked Dec 26 '14 02:12

GreenRaccoon23


2 Answers

Your code does do exactly what you're telling it to do (in simple cases):

>>> other_list = ['foo', 'bar    ', 'baz    ']
>>> filtered_list = [x for x in other_list
...                  if not '    ' in x or '    ' in other_list[int(other_list.index(x) -1)]]
>>> print filtered_list
['foo', 'baz    ']

i.e, it picks all items that either don't contain four spaces, or follow an item that does contain four spaces.

It's really weird code in many ways -- for example, why use .index which will pick the first item equal to x in case of duplicates; and, should the first item be considered to "follow" the last one as this code is saying? But, without a clear explanation in English of what your code is supposed to accomplish, just "reverse engineering" the code as written, it does appear to do exactly what it says in simple cases (first item does not contain four spaces, no duplicates), although very, very inefficiently (O(N squared) where it's trivially simple to make it O(N) with enumerate).

So please supply a clear, exhaustive explanation of what you think you're doing here, and I'll gladly edit this answer to show you how to do what you actually mean, rather than what you (apparently wrongly) coded. But, I can't read your mind, so... (BTW, this does need to be "an answer" since formatting the code above is crucial to its readability, so it can't be "just a comment").

Added: the normal way to "get the item just before the current one in other_list" would of course not use .index (which is O(N) so makes the whole list comprehension O(N squared)!) but rather enumerate. What is "the item just before" the first one requires interpretation, but the normal reading is "there is no such thing".

So suppose you have two functions curp (predicate qualifying the current item, if truthy on it) and prevp (predicate qualifying the current item, if truthy on the previous item if any). Then, the normal way to pick items from list xs would clearly be:

[x for i, x in enumerate(xs) if curp(x) or i>0 and prevp(xs[i-1])]

Of course it works exactly the same way whether the predicates are elegantly encapsulated into functions, as above, or more roughly expressed in-line. So if, e.g:

def curp(x): return 'AB' not in x
def prevp(xp): return 'CD' in xp

then the following listcomp is equivalent to the previous one, just less readable:

[x for i, x in enumerate(xs) if 'AB' not in X or i>0 and 'CD' in xs[i-1]]

Parentheses would be rather redundant here, and not 'AB' in X an even less elegant, readable, and unambiguous way to express 'AB' not in X -- but, feel free to ignore these style opinions, they only come from 15 years' worth of Python experience, after all:-)

like image 197
Alex Martelli Avatar answered Oct 14 '22 14:10

Alex Martelli


You can use multiple ands/ors but only one if. As an example, the following:

In [7]: a = [1,2,3,4,5,6,7,8,9,10]

In [8]: b = [x for x in a if x % 2 == 0 or x % 3 == 0]

In [9]: b
Out[9]: [2, 3, 4, 6, 8, 9, 10]

works just fine.

like image 3
MattDMo Avatar answered Oct 14 '22 15:10

MattDMo