Hjalte
Hjalte

Reputation: 396

Several list comprehensions - one after each other

I have written some code, and to try and grasp the concept of list comprehensions, I am trying to convert some of the code into list comprehensions.

I have a nested for loop:

with (Input) as searchfile:
    for line in searchfile:
        if '*' in line:
            ID = line[2:13]
            IDstr = ID.strip()
            print IDstr
            hit = line
            for i, x in enumerate(hit):
                    if x=='*':
                      position.append(i)
                      print position

I have made the first part of the code into a list comprehension as such:

ID = [line[2:13].strip() for line in Input if '*' in line]
print ID

This works fine. I have tried to do some of the next, but it is not working as intended. How do I make several list comprehensions after each other. The "Hit = …"-part below works fine, if it is the first list comprehension, but not if it is the second. The same with the above - it seems to work only, if it is the first. Why is this?

Hit = [line for line in Input if '*' in line]
print Hit

Positions = [(i, x) for i, x in enumerate(Hit) if x == '*']
print Positions

Upvotes: 1

Views: 130

Answers (1)

tobias_k
tobias_k

Reputation: 82949

it seems to work only, if it is the first. Why is this?

This is because file objects -- input in your case -- are iterators, i.e. they are exhausted once you iterated them once. In your for loop this is not a problem, because you are iterating the file just once for both ID and position. If you want to use two list comprehensions like this, you either have to open the file anew for the second one, or read the lines from the file into a list, and use that list in the list comprehensions.

Also note that your positions list comprehension is wrong, as it enumerates the Hit list, and not each of the elements in the list, as was the case in your loop.

You could try like this (not tested):

# first, get the lines with '*' just once, cached as a list
star_lines = [line for line in input if '*' in line]
# now get the IDs using those cached lines
ids = [line[2:13].strip() for line in star_lines]
# for the positions we need a nested list comprehension
positions = [i for line in star_lines for i, x in enumerate(line) if x == '*']

That nested list comprehension is about equivalent to this nested loop:

positions = []
for line in star_lines:
    for i, x in enumerate(line):
        if x == '*':
            posiitons.append(i)

Basically, you just "flatten" that block of code and put the thing to be appended to the front.

Upvotes: 2

Related Questions