14

I have the code

for iline, line in enumerate(lines):
    ...
    if <condition>:
        <skip 5 iterations>

I would like, as you can read, have the for loop skip 5 iterations if the condition is met. I can be sure that, if the condition is met, there are 5 or more objects left in the "lines" object.

lines exists of an array of dictionaries, which have to be looped over in order

4
  • while loop can be more efficient in this case i think. Commented Jan 25, 2015 at 16:07
  • i need the number of the iteration in the rest of my code, so how would I do it with a while? Commented Jan 25, 2015 at 16:09
  • the continue command moves the loop to the next iterable Commented Jan 25, 2015 at 16:12
  • @kilojoules, that will only skip a single line Commented Jan 25, 2015 at 16:12

6 Answers 6

15
iline = 0
while iline < len(lines):
    line = lines[iline]
    if <condition>:
        place_where_skip_happened = iline
        iline += 5
    iline += 1

If you are iterating over a file object you can skip lines using next or make lines an iterator:

lines = iter(range(20))

for l in lines:
    if l == 10:
        [next(lines) for _ in range(5)]
    print(l)
0
1
2
3
4
5
6
7
8
9
10
16
17
18
19

It really depends on what you are iterating over and what you want to do.

Using your own code with iter and islice:

from itertools import islice


it = iter(enumerate(lines))

for iline, line in it:
    if <condition>:
        place_where_skip_happened = iline
        next(islice(it,5 ,5), None)
    print(line)
Sign up to request clarification or add additional context in comments.

11 Comments

is this really the most beautiful code? Having to add the iline+=1 at the end of your loop?
@pidgey What's wrong with that? Why does it need to be sugarified into some idiom? This is short, to the point, and it's clear from the code what the intention is. Nothing wrong with it at all.
as i put in the question, a list of dictionaries
@PadraicCunningham: but the next(lines) won't work over an enumerate(), or will it?
@pidgey, it will if you call iter on it like I did to range. You can also call itertools.islice on the iterator to skip the lines docs.python.org/2/library/itertools.html#itertools.islice
|
9

The standard idiom for doing this is to make an iterator and then use one of the consumer patterns (see here in the itertools docs.)

For example:

from itertools import islice

lines = list("abcdefghij")

lit = iter(enumerate(lines))
for iline, line in lit:
    print(iline, line)
    if line == "c":
        # skip 3
        next(islice(lit, 3,3), None)

produces

0 a
1 b
2 c
6 g
7 h
8 i
9 j

Comments

6

Using the enumeration index

Similar to the accepted answer… except without using itertools (IMHO islice doesn't improve readability), plus enumerate() already returns an iterator so you don't need the iter() at all:

lines = [{str(x): x} for x in range(20)]  # dummy data

it = enumerate(lines)
for i, line in it:
    print(line)

    if i == 10:  # condition using enumeration index
        [next(it, None) for _ in range(5)]  # skip 5

That last line can optionally be expanded for readability:

        for _ in range(5):  # skip 5
            next(it, None)

The None argument in next() avoids an exception if there aren't enough items to skip. (For the original question, it can be omitted as the OP wrote: "I can be sure that, if the condition is met, there are 5 or more objects left in the lines object.")

Not using the enumeration index

If the skip condition isn't based on the enumeration index, simply treat the list as a FIFO queue and consume from it using pop():

lines = [{str(x): x} for x in range(20)]  # dummy data

while lines:
    line = lines.pop(0)  # get first item
    print(line)

    if <condition>:  # some other kind of condition
        [lines.pop(0) for _ in range(5)]  # skip 5

As before, that last line can optionally be expanded for readability:

        for _ in range(5):  # skip 5
            lines.pop(0)

(For large lists, use collections.deque for performance.)

Comments

1

You could use a functional programming style with recursion, first by putting the necessary parts of your for loop into a function:

def my_function(iline, line, rest_of_lines, **other_args):
    do_some_side_effects(iline, line, **other_args)

    if rest_of_lines == []:
        return <some base case>

    increment = 5 if <condition> else 1
    return my_function(iline+increment, 
                       rest_of_lines[increment-1], 
                       rest_of_lines[increment:],
                       **other_args)

Optionally, if it doesn't need to return anything, you can just adjust those lines of code to be function calls, and the return result will be None.

Then some place you actually call it:

other_args = get_other_args(...)

my_function(0, lines[0], lines[1:], **other_args)

If you need the function to return something different for each index, then I would suggest modifying this slightly to account for the output data structure you want. In that case, you might want to pass the inner result of do_some_side_effects back into the recursive function call so it can build up the result.

def my_function(iline, line, rest_of_lines, output, **other_args):
    some_value = do_some_side_effects(iline, line, **other_args)

    new_output = put_value_in_output(some_value, output)
    # could be as simple as appending to a list/inserting to a dict
    # or as complicated as you want.

    if rest_of_lines == []:
        return new_output

    increment = 5 if <condition> else 1
    return my_function(iline+increment, 
                       rest_of_lines[increment-1], 
                       rest_of_lines[increment:],
                       new_output,
                       **other_args)

Then to call

other_args = get_other_args(...)

empty_output = get_initial_data_structure(...)

full_output = my_function(0, lines[0], lines[1:], empty_output, **other_args)

Note that in Python, because of the way most of the basic data structures are implemented, this programming style is not going to gain you efficiency, and in the context of other object oriented code it may even be bad style to complicate things beyond the simple while solution.

My advice: use the while loop, though I would tend to structure my projects and APIs so that using a recursive functional approach will still be efficient and readable. I would also try not to allow side effects inside the loop.

Comments

0

As Padraic Cunningham states, you can do this with a while loop, you can also use a dictionary to replace the if-statement:

iline = 0
skip = {True:5, False:1}

while iline > len(lines):
    line = lines[iline]
    ...
    iline += skip[condition]

Comments

0

Use an external flag and set it when condition is met and check it in the beginning of cycle:

ignore = 0
for iline, line in enumerate(lines):
    if ignore > 0:
        ignore -= 1
        continue

    print(iline, line)

    if iline == 5:
        ignore = 5

Or explicitly extract 5 elements from enumeration:

enum_lines = enumerate(lines)
for iline, line in enum_lines:
    print(iline, line)

    if iline == 5:
        for _, _ in zip(range(5), enum_lines):
            pass

I personally prefer first approach, but second one looks more Pythonic.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.