Using break in a list comprehension

How can I break a list comprehension based on a condition, for instance when the number 412 is found?

Code:

numbers = [951, 402, 984, 651, 360, 69, 408, 319, 601, 485, 980, 507, 725, 547, 544,
           615, 83, 165, 141, 501, 263, 617, 865, 575, 219, 390, 984, 592, 236, 105, 942, 941,
           386, 462, 47, 418, 907, 344, 236, 375, 823, 566, 597, 978, 328, 615, 953, 345, 399,
           162, 758, 219, 918, 237, 412, 566, 826, 248, 866, 950, 626, 949, 687, 217, 815, 67,
           104, 58, 512, 24, 892, 894, 767, 553, 81, 379, 843, 831, 445, 742, 717, 958, 609, 842,
           451, 688, 753, 854, 685, 93, 857, 440, 380, 126, 721, 328, 753, 470, 743, 527]

even = [n for n in numbers if 0 == n % 2]

So functionally, it would be something you can infer this is supposed to do:

even = [n for n in numbers if 0 == n % 2 and break if n == 412]

I really prefer:

  • a one-liner
  • no other fancy libraries like itertools, “pure python” if possible (read: the solution should not use any import statement or similar)

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

Use a function to raise StopIteration and list to catch it:

>>> def end_of_loop():
...     raise StopIteration
... 
>>> even = list(end_of_loop() if n == 412 else n for n in numbers if 0 == n % 2)
>>> print(even)
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418, 344, 236, 566, 978, 328, 162, 758, 918]

For those complaining it is not a one-liner:

even = list(next(iter(())) if n == 412 else n for n in numbers if 0 == n % 2)

For those complaining it is hackish and should not be used in production code: Well, you’re right. Definitely.

Method 2

You can use generator expressions together with itertools.takewhile():

even_numbers = (n for n in numbers if not n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

Edit: I just noticed the requirement not to use any imports. Well, I leave this answer here anyway.

Method 3

I know this is a VERY OLD post, however since OP asked about using break inside a list-comprehension and I was also looking for something similar, I thought I would post my findings here for future reference.

While investigating break, I came across little known feature of iter as iter(callable, sentinel) which return an iterator that “breaks” iteration once callable function value is equal to sentinel value.

>>> help(iter)
Help on built-in function iter in module __builtin__:

iter(...)
    iter(collection) -> iterator
    iter(callable, sentinel) -> iterator

    Get an iterator from an object.  In the first form, the argument must
    supply its own iterator, or be a sequence.
    In the second form, the callable is called until it returns the sentinel.

Tricky part here is defining a function that would fit given problem. In this case first we need to convert given list of numbers to an iterator using x = iter(numbers) which feeds as external variable into lambda function.

Next, our callable function is just a call to the iterator to spit out next value. The iterator then compares with our sentinel value (412 in this case) and “breaks” once that value is reached.

print [i for i in iter(lambda x=iter(numbers): next(x),412) if i %2 == 0]

>>> 
[402, 984, 360, 408, 980, 544, 390, 984, 592, 236, 942, 386, 462, 418,  
 344, 236, 566, 978, 328, 162, 758, 918]

Method 4

even = [n for n in numbers[:None if 412 not in numbers else numbers.index(412)] if not n % 2]

Just took F.J.’s code above and added a ternary to check if 412 is in the list. Still a ‘one liner’ and will work even if 412 is not in the list.

Method 5

If 412 will definitely be in the list you could use this:

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

If you want to include 412 in the result just use numbers[:numbers.index(412)+1] for the slice.

Note that because of the slice this will be less efficient (at least memory-wise) than an itertools or for loop solution.

Method 6

The syntax for list displays (including list comprehensions) is here: http://docs.python.org/reference/expressions.html#list-displays

As you can see, there is no special while or until syntax. The closest you can get is:

even_numbers = (n for n in numbers if 0 == n % 2)
list(itertools.takewhile(lambda x: x != 412, even_numbers))

(Code taken from Sven Marnach’s answer, posted while I was typing this).

Method 7

another sneaky one-line solution to solve breaking in list comprehension, with the help of end condition.

without using numbers.index(412), maybe a little bit faster?

even = [n for end in [[]] for n in numbers
        if (False if end or n != 412 else end.append(42))
        or not end and not n % 2]

Note: This is a bad idea. just for fun : )

as @WolframH said:

For those complaining it is hackish and should not be used in production code: Well, you’re right. Definitely.

Method 8

Considering the generator solution is outdated I came up with the following:

even = [n for n in next((numbers[:i] for i, n in enumerate(numbers) if n == 412)) if not n % 2]

Then I went back and saw Andrew Clark’s answer which is the same but much better

even = [n for n in numbers[:numbers.index(412)] if not n % 2]

Regardless the best part about the slicing solution is you can choose to include or exclude a number of elements on either side of the ending element for example to get 412 and the number after:

even = [n for n in numbers[:numbers.index(412)+2] if not n % 2]

Method 9

Once I met a similar question on SO, the answer was:

next((x for x in [1, 2, 3, 4] if x % 2 == 0), [])

The last [] needs as default to prevent the StopIteration error if not found

or

any(print(x) if x < 2 else True for x in range(5))

print to prove, he returns None (logically False).


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x