Using break in a list comprehension

PythonList Comprehension

Python Problem Overview


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)

Python Solutions


Solution 1 - Python

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.

Solution 2 - Python

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.

Solution 3 - Python

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]

Solution 4 - Python

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.

Solution 5 - Python

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.

Solution 6 - Python

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).

Solution 7 - Python

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.

Solution 8 - Python

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]

Solution 9 - Python

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).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionFlaviusView Question on Stackoverflow
Solution 1 - PythonReinstate MonicaView Answer on Stackoverflow
Solution 2 - PythonSven MarnachView Answer on Stackoverflow
Solution 3 - PythonAnil_MView Answer on Stackoverflow
Solution 4 - PythonMichael David WatsonView Answer on Stackoverflow
Solution 5 - PythonAndrew ClarkView Answer on Stackoverflow
Solution 6 - PythonMarcinView Answer on Stackoverflow
Solution 7 - PythonrecnacView Answer on Stackoverflow
Solution 8 - PythonAlex CooperView Answer on Stackoverflow
Solution 9 - PythonДавид ШикоView Answer on Stackoverflow