Reputation: 350
I have a list which looks like this:
['G1X0.000Y3.000', 'G2X2.000Y3.000I1.000J2.291', 'G1X2.000Y-0.000', 'G2X0.000Y0.000I-1.000J-2.291']
The formatting is such that if the numeric content after X,Y,I or J are positive there is no + sign but if they are negative then the - sign is included. I am trying to loop through this list and to basically add the + sign if there is no - sign at the start of the numeric content. The result should look like this:
['G1X+0.000Y+3.000', 'G2X+2.000Y+3.000I+1.000J+2.291', 'G1X+2.000Y-0.000', 'G2X+0.000Y+0.000I-1.000J-2.291']
I'm trying to use a list comprehension to do so as follows:
#Make sure that X, Y, I and J start with + or -
for count,i in enumerate(fileContents):
if 'G' in i:
indexOfI = i.index("X")
if(i[indexOfI+1]!="-"):
print(i[:indexOfI+1] + "+" + i[indexOfI+1:])
fileContents[count] = i[:indexOfI+1] + "+" + i[indexOfI+1:]
indexOfY = i.index("Y")
if(i[indexOfY+1]!="-"):
fileContents[count] = i[:indexOfY+1] + "+" + i[indexOfY+1:]
if "G2" in i:
indexOfI = i.index("I")
if(i[indexOfI+1]!="-"):
fileContents[count] = i[:indexOfI+1] + "+" + i[indexOfI+1:]
indexOfJ = i.index("J")
if(i[indexOfJ+1]!="-"):
fileContents[count] = i[:indexOfJ+1] + "+" + i[indexOfJ+1:]
the statement print(i[:indexOfI+1] + "+" + i[indexOfI+1:])
gives an output in the console of:
G1X+0.000Y3.000
G2X+2.000Y3.000I1.000J2.291
G1X+2.000Y-0.000
G2X+0.000Y0.000I-1.000J-2.291
Which shows me that this performs what I want it to, however if I print fileContents
after this function there are no changes to the list. In other words the following line of code does not replace the list item in each position as I expect it to:
fileContents[count] = i[:indexOfI+1] + "+" + i[indexOfI+1:]
Why does this not work when I can do the following and it does update the list correctly?
#Format each command to a 32 byte string
for i, s in enumerate(fileContents):
fileContents[i] =s.ljust(32,'#')
edit: I originally titled the post "Why doesn't using a list comprehension this way replace each item in the list?". Users have kindly pointed out this has nothing to do with a list comprehension. I apologise, I thought this format x in list
was a list comprehension.
Upvotes: 0
Views: 74
Reputation: 350272
if I print
fileContents
after this function there are no changes to the list.
Actually, there are changes, but at most one +
is added (the last one).
This is because you don't apply the same change to i
, which means that the next if
blocks will copy a part from i
back to fileContents[count]
that didn't have the previous change.
The quick fix is to make sure you apply the change also to i
. Something like:
fileContents[count] = i = i[:indexOfI+1] + "+" + i[indexOfI+1:]
# ^^^^
You can perform this task with list comprehension using re.sub
:
import re
fileContents = [re.sub(r"([XYIJ])(?=\d)", r"\1+", line) for line in fileContents]
This will match any X, Y, I or J followed by a digit. In that case, a plus is inserted between those. If you need more strict matching rules, where the line must start with "G", ...etc, then the regular expression will become more complex.
Upvotes: 2
Reputation: 14233
You can just add +
after any of these chars, then replace back +-
(f any) with -
:
def my_replace(item):
for char in 'XYIJ':
item = item.replace(char, f'{char}+')
return item.replace('+-', '-')
spam = ['G1X0.000Y3.000', 'G2X2.000Y3.000I1.000J2.291',
'G1X2.000Y-0.000', 'G2X0.000Y0.000I-1.000J-2.291']
eggs = [my_replace(item) for item in spam] # now, this is list comprehension
print(eggs)
output
['G1X+0.000Y+3.000', 'G2X+2.000Y+3.000I+1.000J+2.291', 'G1X+2.000Y-0.000', 'G2X+0.000Y+0.000I-1.000J-2.291']
Upvotes: 1
Reputation: 14141
In the loop
for i, s in enumerate(fileContents):
you iterate over the fileContents
list, which you want to change in the same loop. It's always dangerous.
Iterate over a copy of this list, which you may simply create by adding [:] to it:
for i, s in enumerate(fileContents[:]):
Upvotes: 1