How to assign the last character of a string to a variable? - python

I'm trying to store the last character of a string in a variable without knowing beforehand how long the string is (the string is originally read from a list). I have:
last = record[-1]
lastInd = len(last) - 1
lastChar = last[lastInd]
But I get the following error:
lastChar = last[lastInd]
IndexError: string index out of range
If I try:
lastChar = last[-1]
I get the same error:
lastChar = last[-1]
IndexError: string index out of range
I don't really understand what's going wrong here? Am I not getting the index right?

As inspectorG4dget says, the only way you can get this exception from last[-1] is if last is an empty string.
If you want to know how to deal with it… well, it depends on what you're trying to do.
Normally, if you're trying to get the last character of a string, you expect it to be non-empty, so it should be an error if it is unexpectedly empty.
But if you want to get the last character if the string is npt empty, or an empty string if it is, there are three ways to do it, in (what I think is) declining order of pythonic-ness, at least in code written by a novice.
First, there's EAFP ("Easier to Ask for Forgiveness than Permission). Assume it'll work, try it, and deal with unexpected failure as appropriate:
try:
lastChar = last[-1]
except IndexError:
lastChar = ''
Then there's LBYL (Look Before You Leap). Check for unexpected cases in advance:
if last:
lastChar = last[-1]
else:
lastChar = ''
Finally, there's being overly clever. Write code that you won't understand three months from now without thinking it through:
lastChar = last[-1:]
This returns all characters from the last one to the end. If there are any characters, that's the same as the last one. If there are no characters, that's nothing.
Note that this only really works as intended because a string's individual elements are themselves strings. If you tried to get the last element from a list like this, you'd get a list of 1 element or an empty list, not a single possible-empty element.

This is because last is an empty string.
Check this out:
>>> last = 'a'
>>> last[-1]
'a'
>>> last = ''
>>> last[-1]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: string index out of range
If you want an empty string for your last character, just in case you have an empty string as your last record, then you could try this:
if last == '': # or `if not last`
lastChar = ''
Alternatively, you could use the ternary operator:
lastChar = last[-1] if last else ''

Related

how to delete char after -> without using a regular expression

Given a string s representing characters typed into an editor,
with "->" representing a delete, return the current state of the editor.
For every one "->" it should delete one char. If there are two "->" i.e "->->" it should delete 2 char post the symbol.
Example 1
Input
s = "a->bcz"
Output
"acz"
Explanation
The "b" got deleted by the delete.
Example 2
Input
s = "->x->z"
Output
empty string
Explanation
All characters are deleted. Also note you can type delete when the editor
is empty as well.
"""
I Have tried following function but id didnt work
def delete_forward(text):
"""
return the current state of the editor after deletion of characters
"""
f = "->"
for i in text:
if (i==f):
del(text[i+1])
How can i complete this without using regular expressions?
Strings do not support item deletion. You have to create a new string.
>>> astring = 'abc->def'
>>> astring.index('->') # Look at the index of the target string
3
>>> x=3
>>> astring[x:x+3] # Here is the slice you want to remove
'->d'
>>> astring[0:x] + astring[x+3:] # Here is a copy of the string before and after, but not including the slice
'abcef'
This only handles one '->' per string, but you can iterate on it.
Here's a simple recursive solution-
# Constant storing the length of the arrow
ARROW_LEN = len('->')
def delete_forward(s: str):
try:
first_occurence = s.index('->')
except ValueError:
# No more arrows in string
return s
if s[first_occurence + ARROW_LEN:first_occurence + ARROW_LEN + ARROW_LEN] == '->':
# Don't delete part of the next arrow
next_s = s[first_occurence + ARROW_LEN:]
else:
# Delete the character immediately following the arrow
next_s = s[first_occurence + ARROW_LEN + 1:]
return delete_forward(s[:first_occurence] + s[first_occurence + ARROW_LEN + 1:])
Remember, python strings are immutable so you should instead rely on string slicing to create new strings as you go.
In each recursion step, the first index of -> is located and everything before this is extracted out. Then, check if there's another -> immediately following the current location - if there is, don't delete the next character and call delete_forward with everything after the first occurrence. If what is immediately followed is not an arrow, delete the immediately next character after the current arrow, and feed it into delete_forward.
This will turn x->zb into xb.
The base case for the recursion is when .index finds no matches, in which case the result string is returned.
Output
>>> delete_forward('ab->cz')
'abz'
>>> delete_forward('abcz')
'abcz'
>>> delete_forward('->abc->z')
'bc'
>>> delete_forward('abc->z->')
'abc'
>>> delete_forward('a-->b>x-->c>de->f->->g->->->->->')
'a->x->de'
There could be several methods to achieve this in python e.g.:
Using split and list comprehensions (If you want to delete a single character everytime one or more delete characters encountered):
def delete_forward(s):
return ''.join([s.split('->')[0]] + [i[1:] if len(i)>1 else "" for i in s.split('->')[1:]])
Now delete_forward("a->bcz") returns 'acz' & delete_forward("->x->z") returns ''. ote that this works for EVERY possible case whether there are many delete characters, one or none at all. Moreover it will NEVER throw any exception or error as long as input is str. This however assumes you want to delete a single character everytime one or more delete characters encountered.
If you want to delete as many characters as the number of times delete characters occur:
def delete_forward(s):
new_str =''
start = 0
for end in [i for i in range(len(s)) if s.startswith('->', i)] +[len(s)+1]:
new_str += s[start:end]
count = 0
start = max(start, end)
while s[start:start+2] =='->':
count+=1
start+=2
start += count
return new_str
This produces same output for above two cases however for case: 'a->->bc', it produces 'a' instead of 'ac' as produced by first function.

Receiving an exit code error on codewars when trying to manipulate a string

Please do not include any spoilers if possible.
I'm working on Kata Exclamation marks series #1: Remove an exclamation mark from the end of string and have been receiving an IndexError: string index out of range despite my code executing properly.
The purpose of the program is to remove an exclamation mark from the end of a string if it exists.
def remove(s):
length = len(s)
if s[len(s)-1] == '!':
new = slice(0,-1,)
return(s[new])
else:
return(s)
The error comes up at:
if s[len(s)-1] == '!':
If anyone could explain why I'm getting this error that would be much appreciated!
Your code crashes if you enter an empty string (you will get an IndexError.)
Moreover, it's not very pythonic and you have an unused variable. For example, you don't need the parentheses after return, and [-1] lets you directly access the last element of the string. Here's a way to write this:
def remove(s):
if s and s[-1] == '!':
return s[:-1]
return s
if s will check if the input string is different than '' to make sure that the input isn't an empty string, then if checks if '!' is in there.
You could also use str.endswith() as user Boris pointed out (which I was not aware of before.)
Edit: if you need me to remove the code to not have any spoilers I gladly will, but your post itself contains an attempt at a model solution to begin with.
Try this
def remove(s):
return s[:-1] if s.endswith('!') else s
You will get that error if you call your function with an empty string, ie:
def remove(s):
length = len(s)
if s[len(s)-1] == '!':
new = slice(0,-1,)
return(s[new])
else:
return(s)
remove("")
Result:
Traceback (most recent call last):
File "/Users/steve/Development/Inlet/inletfetch_if2/test/behave/features/Blah.py", line 9, in <module>
remove("")
File "/Users/steve/Development/Inlet/inletfetch_if2/test/behave/features/Blah.py", line 3, in remove
if s[len(s)-1] == '!':
IndexError: string index out of range
This occurs because you're asking for the last character in the string, and since there are no characters in the string, there is no last character in the string.

How do I remove specific elements from a string without using replace() method?

I'm trying to traverse through a string using its indices and remove specific elements. Due to string length getting shorter as elements are removed, it always goes out of range by the time the final element is reached.
Here's some code to ilustrate what I'm trying to do. For example, going from "1.2.3.4" to "1234".
string = "1.2.3.4"
for i in range(len(string)):
if string[i] == ".":
string = string[:i] + string[i+1:]
I know there are alternate approaches like using string method called replace() and I can run string = string.replace(string[i], "", 1) OR I can traverse through individual elements (not indicies).
But how would I solve it using the approach above (traversing string indices)? What techniques can I use to halt the loop after it reaches the final element of the string? Without continuing to advance the index, which will go out of range as elements are removed earlier in the string.
Use this:
string = "1.2.3.4"
res = ""
for s in string:
if s != '.':
res += s
The result is of course '1234'.
you can use the re module:
import re
string = "1.2.3.4"
string = re.sub('\.','',string)
print(string)
If I understand correctly, you want to modify a string by its index while the length of it keep changing.
That's pretty dangerous.
The problem you ran into is caused by range(len(string)).See, once the range is fixed, it won't change.And in the loop, string changes, it gets shorter, and that's why you got out of range error.
So what you want to do is to track the string while looping, and use if-else to find the '.'s, here is an example:
string = '1.2.3.4'
i = 0
while i < len(string):
if string[i] == '.':
string = string[:i] + string[i+1:]
else:
i += 1
Still, there are plenty of ways to deal with your string, don't use this, this is not good.
it could be done like this (with a try/except block), but that's not really a great way to approach this problem (or any problem)
string = "1.2.3.4"
for i in range(len(string)):
try:
if string[i] == ".":
string= string[:i]+string[i+1:]
except:
IndexError
result is 1234
The only real change of course is that by adding a try/except around our loop, we save ourselves from the IndexError that would normally come up once we try to access an element in the string that is now out of bounds
Once that happens, the Exception is caught and we simply exit the loop with our finished string

Code wars : Title Case with python

def title_case(title, minor_words = 0):
title = title.lower().split(" ")
title_change = []
temp = []
if minor_words != 0 :
minor_words = minor_words.lower().split(" ")
for i in range(len(title)):
if (i != 0 and title[i] not in minor_words) or (i == 0 and title[i] in minor_words):
temp = list(title[i].lower())
temp[0] = temp[0].upper()
title_change.append("".join(temp))
else:
title_change.append(title[i])
temp = []
else:
for i in range(len(title)):
temp = list(title[i])
temp[0] = temp[0].upper()
title_change.append("".join(temp))
temp = []
return " ".join(title_change)
Hello,this is my python code here.
This is the question:
A string is considered to be in title case if each word in the string is either (a) capitalised (that is, only the first letter of the word is in upper case) or (b) considered to be an exception and put entirely into lower case unless it is the first word, which is always capitalised.
Write a function that will convert a string into title case, given an optional list of exceptions (minor words). The list of minor words will be given as a string with each word separated by a space. Your function should ignore the case of the minor words string -- it should behave in the same way even if the case of the minor word string is changed.
I am trying not to use capitalize() to do this.It seems my code works fine on my computer,but the code wars just prompted "IndexError: list index out of range".
Your code will break if title has leading or trailing spaces, or two consecutive spaces, such as "foo bar". It will also break on an empty string. That's because title.lower().split(" ") on any of those kinds of titles will give you an empty string as one of your "words", and then temp[0] will cause an IndexError later on.
You can avoid the issue by using split() with no argument. It will split on any kind of whitespace in any combinations. Multiple spaces will be treated just like one space, and leading or trailing whitespace will be ignored. An empty string will become an empty list when split is called, rather than a list with one empty string in it.
Just as a supplement to #Blckknght's explanation, here is an illuminating console session that steps through what's happening to your variable.
>>> title = ''
>>> title = title.lower().split(' ')
>>> title
['']
>>> temp = list(title[0])
>>> temp
[]
>>> temp[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
I tried your solution on other (non-whitespace) inputs, and it works fine.

python string manipulation, finding a substring within a string [duplicate]

This question already has answers here:
How do I get a substring of a string in Python? [duplicate]
(16 answers)
Closed 8 years ago.
I am trying to find a substring within a larger string in python. I am trying to find the text present after the string "Requests per second:" is found. It seems my knowledge of python strings and python in general is lacking.
My error is on the 3rd line of code minusStuffBeforeReqPer = output[reqPerIndx[0], len(output)], I get the error that without the [0] on reqPerIndx I am trying to access a tuple, but with it I get the error that I int object has no attribute __getitem__. I am trying to find the index of the start of the reqPerStr in the output string.
The code
#output contains the string reqPerStr.
reqPerStr = "Requests per second:"
reqPerIndx = output.find(reqPerStr)
minusStuffBeforeReqPer = output[reqPerIndx[0], len(output)]
eolIndx = minusStuffBeforeReqPer.find("\n")
semiColIndx = minusStuffBeforeReqPer.find(":")
instanceTestObj.reqPerSec = minusStuffBeforeReqPer[semiColIndx+1, eolIndx]
You must use output[begin:end], not output[begin, end] (that's just how the syntax for slicing ordinary strings/lists/etc works). So:
minusStuffBeforeReqPer = output[reqPerIndx:len(output)]
However, this is redundant. So you should instead probably do this:
minusStuffBeforeReqPer = output[reqPerIndx:]
By omitting the end part of the slice, the slice will go all the way to the end of output.
You get a error about accessing a tuple without the [0] because you have passed a tuple (namely (reqPerIndx, len(output)) to the slicing [...]), and you get an error about int having no __getitem__ because when you write reqPerIndx[0], you are trying to get the 0th element of reqPerIndx, which is an integer, but there is of course no such thing as the "0th element of an integer", because integers do not have elements.
As #AshwiniChaudhary points out in the comments, str.find will return -1 if the substring is not found. If you are certain that the thing you're looking for will always be found somewhere in output, I suppose you don't need to handle the -1 case, but it might be a good idea to do so anyway.
reqPerIndx = output.find(reqPerStr)
if reqPerIndx != -1:
minusStuffBeforeReqPer = ...
# etc
else:
# handle this case separately
You might have better luck with regexes. I don't know what output looks like, so I sort of just guessed - you should adapt this to match whatever you have in output.
>>> import re
>>> re.findall(r'(?:Requests per second:)\s*(\d+)', "Requests: 24")
[]
>>> re.findall(r'(?:Requests per second:)\s*(\d+)', "Requests per second: 24")
['24']
You have the error on those two lines:
minusStuffBeforeReqPer = output[reqPerIndx[0], len(output)]
instanceTestObj.reqPerSec = minusStuffBeforeReqPer[semiColIndx+1, eolIndx]
You have to use the : to create a range. start:end.
You can omit the last parameter to get to the end or omit the first parameter to omit the begining. The parameters can be negative number too. Since find might return -1 you'll have to handle it differently because if the string isn't found, you'll end up with:
minusStuffBeforeReqPer = output[-1:]
Which is the last char in the string.
You should have code that looks like this:
#output contains the string reqPerStr.
reqPerStr = "Requests per second:"
reqPerIndx = output.find(reqPerStr)
if reqPerIndx != -1:
minusStuffBeforeReqPer = output[reqPerIndx[0]:]
eolIndx = minusStuffBeforeReqPer.find("\n")
semiColIndx = minusStuffBeforeReqPer.find(":")
if eolIndx > semiColIndx >= 0:
instanceTestObj.reqPerSec = minusStuffBeforeReqPer[semiColIndx+1:eolIndx]
This is good but, you should definitely change the code with a regex. As I understand, you really want to match a string that starts with reqPerStr and ends with \n and get everything that is in between : and \n.
You could do that with such pattern:
"Requests per second:(.*)\n"
You'll end up with:
import re
reqPerIndx = output.find(reqPerStr)
match = re.match("Requests per second:(.*)\n", output)
if match:
instanceTestObj.reqPerSec = match.group(1)
If you want to find all matches, you can do that:
for match in re.finditer("Requests per second:(.*)", output)
instanceTestObj.reqPerSec = match.group(1)

Categories

Resources