how to modify strings in a list of strings - python

I would like to take a list of strings which represent individual lines entered in a CLI and put '~$ ' at the beginning so when I display them it is more clear that they are command lines. I tried this
command = # a multiline block of command lines
lines = command.split('\n')
for l in lines:
l = '~$ ' + l
for l in lines: print l
But this modifies the temporary variable l I think without going back and changing the actual value in the list. If I put the print inside of the first loop it prints with the correct values, but if I do it like shown the change isn't made. Thanks in advance for any help.

Use a list comprehension:
lines = ['~$ ' + line for line in command.split('\n')]
If you have to use a for loop, you'd use enumerate() to include an index so you can replace the individual items in the list:
for i, line in enumerate(lines):
lines[i] = '~$ ' + line

The functional way:
list(map(lambda s: '~$ ' + s, command.splitlines()))

Related

Remove multiple spaces from python list elements

I have a list with spaces within the string. How can I remove these spaces.
['ENTRY', ' 102725023 CDS T01001']
I would like to have the final list as:
['ENTRY', '102725023 CDS T01001']
I tried the strip() function but the function is not working on list. Any help is highly appreciated.Remo
Suppose this is you string
string = "A b c "
And you want it in this way
Abc
What you can do is
string2 = " ".join(string.split())
print(string2)
The easiest way is to build a new list of the values with the spaces removed. For this, you can use list comprehensions and the idiom proposed by #CodeWithYash
old_list = ['ENTRY', ' 102725023 CDS T01001']
new_list = [" ".join(string.split()) for s in old_list]
Note that this works because the default behavior of split is:
split according to any whitespace, and discard empty strings from the result.
If you would want to remove anything but whitespace, you would have to implement you own function, maybe using regular expression.
Note also that in Python strings are immutable: you can not edit each item of the list in place. If you do not want to create a new list (for example, if a reference to the list is kept in other place of the program), you can change every item:
l = ['ENTRY', ' 102725023 CDS T01001']
for i, s in enumerate(l):
old_list[i] = " ".join(s.split())
print(l)
Output:
['ENTRY', '102725023 CDS T01001']
I wrote this function:
s = " abc def xyz "
def proc(s):
l = len(s)
s = s.replace(' ',' ')
while len(s) != l:
l = len(s)
s = s.replace(' ',' ')
if s[0] == ' ':
s = s[1:]
if s[-1] == ' ':
s = s[:-1]
return s
print(proc(s))
the idea is to keep replacing every two spaces with 1 space, then check if the first and last elements are also spaces
I don't know if there exists an easier way with regular expressions or something else.

Why doesn't using a for loop this way replace each item in the list?

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.
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.
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[:]):
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']

What are some other alternatives to getting rid of the extra blankline?

I'm using a for loop and it concatenates a string as such:
list1 = ["Hi","There"]
final = ""
for item in list1:
final += item + "\n"
It prints out:
Hi
There
#extra space here instead of comment
Now I know the solution where you do for i in range(len(list1)) and then you use "i" in an if statement to check if it's the last line, but this is problematic for me in my bigger case (which is not shown here).
What are some other alternatives to getting rid of the extra blankline? Like a trim or regex? I don't know, just suggestions.
You can use join to insert newlines only between each element of the list:
final = '\n'.join(list1)
Or you can use strip on the output of your loop:
list1 = ["Hi","There"]
final = ""
for item in list1:
final += item + "\n"
final = final.strip()
you can use end=""
Suppose a is a list
eg: print(a,end="")

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

Basic Python loop question - problem reading a list from a text file

I'm trying to read a list of items from a text file and format with square brackets and separators like this: ['item1','item2', .... 'last_item'] but I'm having trouble with the beginning and end item for which I always get: ...,'last_item','], so I do not want the last ,' to be there.
In python I've write:
out_list = "['"
for line in open(file_in):
out_list += line #append the item to the list
out_accession_list += "','" #add the separator
out_accession_list += "]" #add the final closed bracket
return out_list
I realize that this is a basic loop question, but I can't think of the best way to do it. Should I use a try final statement, should it be a while loop, or should I count the number of lines first and then use a loop with a range?
Help much appreciated.
Thanks,
John
Read in all your lines and use the string.join() method to join them together.
lines = open(file_in).readlines()
out_list = "['" + "','".join(lines) + "']"
Additionally, join() can take any sequence, so reading the lines isn't necessary. The above code can be simplified as:
out_list = "['" + "','".join(open(file_in)) + "']"
out_list = []
for line in open(file_in):
out_list.append("'" + line + "'")
return "[" + ",".join(out_list) + "]"
You "right strip" for "," the result before adding the last "]".
e.g. use the string.rstrip(",")
Or
result = "['"
for line in open(file_in):
if len(result) > 0:
result += "','"
result += line
result += "']"
return result
def do(filepath):
out = []
for line in open(filepath, 'r'):
out.append("'" + line.strip() + "'")
return out.__str__()
Your desired output format is exactly Python's standard printable representation of a list. So an easy solution is to read the file, create a list with each line as a string element (stripping the end-of-line of each), and call the Python built-in function repr to produce a string representation of the list:
>>> repr([line.rstrip() for line in open(file_in)])
"['item1', 'item2', 'item3']"

Categories