How do I set characters in a Python string? - python

I have a function that decrements a whole number parameter represented by a string. For instance, if I pass in the string "100", it should return "99."
def dec(s):
i = len(s) - 1
myString = ""
while (i >= 0):
if s[i] == '0':
s[i] = '9'
i -= 1
else:
s[i] = chr(int(s[i]) - 1)
break
return s
However, Python issues this error.
s[i] = '9'
TypeError: 'str' object does not support item assignment
I am assuming that s[i] cannot be treated as an lvalue. What is a solution around this?

You can do:
s = s[:i] + "9" + s[i+1:]
This takes the part of the string before character index i, appends a 9, then appends the part of the string after character index i. However, doing a lot of string appends like this is not terribly efficient.
The other answer is that if you're dealing with numbers, why not actually use numbers instead of strings?
def dec(s):
return str(int(s) - 1)

Strings aren't mutable, but lists are. You can easily convert the string to a list of individual characters:
l = list(s)
Then convert it back:
s = ''.join(l)
Since you're working with a numeric string there are more direct approaches, but this answer works for the general case.

You can't. In Python, strings are immutable -- once created, they can't be changed.
You have two options without changing your function entirely.
Convert the string to a list and back:
def dec(s):
s = list(s)
i = len(s) - 1
myString = ""
while (i >= 0):
if s[i] == '0':
s[i] = '9'
i -= 1
else:
s[i] = chr(int(s[i]) - 1)
break
return ''.join(s)
Create a new string each time you want to make a change:
def dec(s):
i = len(s) - 1
myString = ""
while (i >= 0):
if s[i] == '0':
s = s[:i] + "9" + s[i+1:]
i -= 1
else:
s = s[:i] + chr(int(s[i]) - 1) + s[i+1:]
break
return s

I'm not sure why you are playing with the string character by character. Isn't this simpler?
def decrement_string(s):
try:
i = int(s)
i = i - 1
return str(i)
except:
# do something else
return "that's no number!"
while True:
s = raw_input("give me a number and I'll decrement it for you: ")
print decrement_string(s)

The solution to your specific problem of "decrementing" strings is to convert s to an int with int(s), decrease that, and convert back to a str: str(int(s)-1).
In [175]: str(int('100')-1)
Out[175]: '99'
The general solution is to not attempt to alter the elements of a string; use some other type to represent your work, and convert to a string as the last step.

Python strings are immutable so you cannot modify them. You have to create a new string that contains the value that you need. You are better off converting the string to an integer, decrementing it, and then converting it back to an integer again.
The reason for immutable strings is primarily efficiency.

Related

Validate string

I am trying to solve the following problem:
Given a string str, the task is to check if the string is a valid
identifier or not. In order to qualify as a valid identifier,
the string must satisfy the following conditions:
It must start with either underscore(_) or any of the
characters from the ranges [‘a’, ‘z’] and [‘A’, ‘Z’].
There must not be any white space in the string.
And, all the subsequent characters after the first character
must not consist of any special characters like $, #, % etc.
Examples:
Input: str= “_code123”
Output: Valid
Input: str = “123code_”
Output: Invalid
My attempted solution, which works:
def isValid(str1, n):
# If first character is invalid
if (((ord(str1[0]) >= ord('a') and
ord(str1[0]) <= ord('z')) or
(ord(str1[0]) >= ord('A') and
ord(str1[0]) <= ord('Z')) or
ord(str1[0]) == ord('_')) == False):
return False
# Traverse the string for the rest of the characters
for i in range(1, len(str1)):
if (((ord(str1[i]) >= ord('a') and
ord(str1[i]) <= ord('z')) or
(ord(str1[i]) >= ord('A') and
ord(str1[i]) <= ord('Z')) or
(ord(str1[i]) >= ord('0') and
ord(str1[i]) <= ord('9')) or
ord(str1[i]) == ord('_')) == False):
return False
# is a valid identifier
return True
# Driver code
str1 = "_code123"
n = len(str1)
if (isValid(str1, n)):
print("Valid")
else:
print("Invalid")
How can I make the above code follow OOP principles, without so many nested ifs?
Have some read on regex it will help a lot for this kind of problem.
https://regexr.com/
This site is great for practice
import re
def isValid(str1):
res=re.match(r'^[_A-Za-z]\w+$',str1)
return res
str1 = "_code123"
n = len(str1)
if (isValid(str1)):
print("Valid")
else:
print("Invalid")
I think you can use that code.
if re.match('^[a-zA-Z_]+', line) is not None and re.match('^[\w-]+$', line) is not None:
return True
[a-zA-Z_]+ Your string may start with a-z, A-Z and underscore
There is nothing to say that nested ifs break OOP principles.
On the other hand, Python has certain features to help you out. First, you don't need to use ord so much because comparing two one-character strings does the same thing:
ord('a') < ord('b')
is equivalent to
'a' < 'b'
Second, Python allows you to chain math comparison operators, so instead of
'a' < 'b' and 'b' < 'c''
you can just type
'a' < 'b' < 'c'
But there is still an easier way: using the isalpha and isalphanumeric methods, you can avoid using the comparison operators, converting your code to:
Also, Python allows you to work directly on each character while looping by using the for c in str construct, saving you much indexing.
A more natural implementation of your code would be:
def is_valid(str1, n):
if not (str1[0] == '_' or str1[0].isalpha()):
return False
for c in str1[1:]:
if not (c == '_' or c.isalnum()):
return False
return True
You could even use a list comprehension for the second part, by doing:
if not all(c == '_' or c.isalnum() for c in str1[1:]):
return False

even and uneven letter, TypeError: not all arguments converted during string formatting

def ul(a):
if a %2 == 0:
print(a.upper())
else:
print(a.lower())
ul(input())
I get following error:
TypeError: not all arguments converted during string formatting
I want to write every even letter in uppercase and every uneven letter in lowercase
What I'm doing wrong?
What you are now doing with this function is check whether the input (a string) is divisible by 2. This is not really what you wanted to do and it raises an error because strings are not modulo divisible.
You should better loop through the indices of the input string to fill a second string with upper and lower case letters:
def ul(s):
outstring = ''
for i in range(len(s)): ## For each index of the string
if i%2==0:
outstring += s[i].upper() ## Even indices become upper case
else:
outstring += s[i].lower() ## Odd indices become lower case
print(outstring)
ul(input("Enter a string: "))
You are trying to get mode of string as I understand. You should use len(a).
def ul(a):
if len(a) %2 == 0:
print(a.upper())
else:
print(a.lower())
ul(input())
Try this
def ul(a):
a = a.lower()
if ord(a) %2 == 0:
print(a.upper())
else:
print(a.lower())
if you want user to enter only Alphabets, then try this
def ul(a):
if ord(a) >= 110 and ord(a) < 123:
a = a.lower()
if ord(a) %2 == 0:
print(a.upper())
else:
print(a.lower())
else:
print("Please enter Alphabets only")
Input will give you a string. You want to use the modulo operator %, which is exactly how you usually find the evenness of a number. So, you are on the right track!
What actually happens is that Python interprets your % as the format operator (probably has a fancy name).
number = 2
print("Number %d is this" % number)
>>> Number 2 is this
Strings in Python are immutable, so you can't just change the string that easily.
Try this Replacing every 2nd character in a string , where you construct a new string by adding all the individual characters together.
def ul(word):
new = []
for i in range(len(word)):
if i%2 == 0:
new += word[i].upper()
else:
new += word[i].lower()
print("".join(new))
This will go through the string character by character, transform the individual character based on if it is even or not, and then construct a list "new", that holds all the individual characters. In the end, just join it, so create a new string out of the list.
There are more pythonic ways to do this using list comprehension, but for now this is easier to read and understand.

Given a string S, remove consecutive duplicates from it recursively using python

i am adding str[1] that is causing one repeated element to be left but if donot do that string does not get printed. any solutions
def removeCD(str):
l = len(str)
if l == 0 or l == 1:
return str
if(str[0]==str[1]):
return str[1] + removeCD(str[2:])
else:
return str[0] + removeCD(str[1:])
string = input().strip()
print(removeCD(string))
When characters are equal, you again adding duplicate character. This should work:
def removeCD(str):
l = len(str)
if l == 0 or l == 1:
return str
if(str[0]==str[1]):
return removeCD(str[1:])
else:
return str[0] + removeCD(str[1:])
string = input().strip()
print(removeCD(string))
Here is the case analysis we need to perform -
If string length is less than two (base case), there is nothing to compare, simply return the string
Otherwise (by induction) the string is at least two characters long. If the first matches the second, drop the first letter and return the recursive result
Otherwise (by induction) the string is at least two characters long and the first two characters do not match. Return the first char combined with the recursive result.
This encodes to a python program in a straightforward way -
def remove_adjacent_dupes (s = ""):
if len(s) < 2:
return s
elif s[0] == s[1]:
return remove_adjacent_dupes(s[1:])
else:
return s[0] + remove_adjacent_dupes(s[1:])
print(remove_adjacent_dupes("bookkeeper"))
# bokeper
When the characters are equal, you should be recursing on everything but the first character:
if(str[0]==str[1]):
return removeCD(str[1:])
def remove(string):
l = len(string)
if l==0 or l==1:
return string
if string[0] == string[1]:
s = remove(string[1:])
return s
else:
s = remove(string[1:])
return string[0]+s
string = input().strip()
print(remove(string))

Print odd index in same line

I'm trying to complete challenge on HackerRank ( Day 6 : Let's review!) and I only did to print the even numbers on the same line, but I can't print the odd indexes that would be needed to complete the challenge.
This is my code:
word_check = input()
for index, char in enumerate (word_check):
if (index % 2 == 0):
print( char ,end ="" )
This is the most specific task:
Given a string, S , of length N that is indexed from 0 to N -1 , print its even-indexed and odd-indexed characters as space-separated strings on a single line.
Thanks!!!
RavDev
You can use slice notation for indexing the original string:
word_check[::2] + " " + word_check[1::2]
[::2] means "start at the beginning and skip every second element until we reach the end" and [1::2] means "start at the second element and skip every second element until we reach the end". Leaving out either start or stop arguments of the slice implies beginning or end of the sequence respectively. Leaving out the step argument implies a step size of 1.
Slice notation is a better approach, but if you want to use for loop and stick to your approach, you can do in this way:
even =''
odd=''
for index, char in enumerate (word_check):
if (index % 2 == 0):
even += char
else: odd += char
print (even, odd)
I am currently trying to solve the same problem. To get your answers on the same line, initiate two strings: one for even and one for odd. If the character's index is even, add it to the even string and vice versa. Here is my working code so far:
def indexes(word,letter):
result = list()
for i,x in enumerate(word):
if x == letter:
result.append(i)
return result
T = int(input())
if T <= 10 and T>= 1:
for i in range(T):
evenstring = ""
oddstring = ""
lastchar = False
S = input()
if len(S) >= 2 and len(S) <= 10000:
for index, char in enumerate (S):
if (index % 2 == 0):
evenstring += char
else: oddstring += char
if len(indexes(S, char)) > 1:
evenstring.replace(evenstring[evenstring.rfind(char)], '')
oddstring.replace(oddstring[oddstring.rfind(char)], '')
print(evenstring, oddstring)
Your next problem now is trying to remove any reoccurrences of duplicate letters from your final answer (they show up in other test cases)

caesar cipher object doesn't support assignment python

Write a method that takes in an integer offset and a string.
Produce a new string, where each letter is shifted by offset. You
may assume that the string contains only lowercase letters and
spaces.
def caesar_cipher(offset, string):
words = string.split(" ")
word_i = 0
while word_i < len(words):
word = words[word_i]
letter_i = 0
while letter_i < len(word):
char_i = ord(word[letter_i]) - ord("a")
new_char_i = (char_i + offset) % 26
word[letter_i] = chr(ord("a") + new_char_i) #----error is at this line ----
letter_i += 1
word_i += 1
return words.join(" ")
print caesar_cipher(3, "abc")
print should return def.
would like to know how to fix this str assignment error please and thank you!
Strings are immutable.
a = "hello"
a[1] = 'q'
..results in this error:
TypeError: 'str' object does not support item assignment
A better approach is to build up new a list of 1-character strings out of the substituted letters. In idiomatic Pythonic, that would be a list comprehension, but a for-loop would also work fine.
Lastly, join them into a new string like this:
''.join(cipher_chars)

Categories