Modify characters between two symbols in a string - python

I have to write a function that receives a string and returns that string with the characters between "*" in uppercase, for example given that string: “I want *this text* to be uppercase”, it returns : “I want THIS TEXT to be uppercase”.
Here is the code that I have written:
l = []
def func(s):
inside = False
for i in s:
if i == "*" and not inside:
inside = True
while inside:
if i == "*":
inside = False
else:
i.upper()
l.append(i)
print(s)
When I run the program it prints out the text without any change.
What am I doing wrong?

By splitting the string at the character * then the uppercase entry is the odd one, then joining together. It is supposed that there are always an even number of * so it should be considered a kind of reserved character.
s = "I want *this text* to be uppercase and *this* as well"
print(' '.join((j.upper() if i%2!=0 else j for i, j in enumerate(s.split('*')))))
Output
I want THIS TEXT to be uppercase and THIS as well

First, this function isn't doing anything to s, so print(s) will print whatever was input, unchanged.
Second, the while inside loop needs to be outside of that if statement - right now, it's only accessed when i is an asterisk. Decrease that whole block's indent by one, and change while inside to if inside - that way, that code is executed for all values of i when inside is true.
Next, you need a way to signal when a second asterisk is reached, or a third, and so on. How about instead of assigning inside to True if i is an asterisk and inside is False, you swap the value of inside every time an asterisk is reached?
if i == '*':
inside = not inside
This negates any need to mess with changing inside in the second part of your code - it puts all the determining "am I inside/am I outside" logic in one place.
Next, you've declared a list l outside the scope of the function, and it looks like you want to add the edited characters to it so the answer you want is in that list in the end. But you want your answer in a string, not a list, and it's usually bad practice to declare a list in global scope and then edit it during a function call (if you call the function more than once, the list will get messy!). Declare an empty string l = '' at the beginning of the function, and then instead of appending characters to it, you can add them using the += operator.
You also need to make sure you're adding the uppercase version, l += i.upper() or the regular version, l += i, depending on whether or not inside is true. You can put all the code after the if i == '*' line in an else statement to catch all cases that i isn't an asterisk.
Putting it all together, your function can look something like this:
def func(s):
l = '' # the string that will be returned (the result of the function)
inside = False
for i in s:
if i == "*": # flip value of inside whenever you see an asterisk
inside = not inside
else: # otherwise, add i to the result
if inside:
l += i.upper() # uppercase if between two asterisks
else:
l += i # unchanged if not
return l # and return the modified string
Then, to test the function:
my_string = "I want *this text* to be uppercase"
my_string_modified = func(my_string)
print(my_string)
print(my_string_modified)
Output:
I want *this text* to be uppercase
I want THIS TEXT to be uppercase
There are definitely more "advanced" ways to do this, as pointed out in other answers on this post, but I hope this answer has helped to clarify what's going wrong in your code, how to fix it, and what some good practices are when you write this kind of thing. Writing code this way is, in my opinion, a really good way to understand how algorithmic processes can be designed and implemented.

I think you've tried to make this more complicated than it is. You need to find the index of both asterisks. From that you can get three slices of the original string and apply upper() to the slice between the asterisks. Note that this code will fail if there are fewer than two asterisks in the string.
def dotricks(s):
i = s.index('*')
j = s.index('*', i+i)
return s[0:i] + s[i+1:j].upper() + s[j+1:]
print(dotricks('I want *this text* to be uppercase'))

You are not changing the string in your code. In this edit below, I've assigned the letters of the string to a new variable. And used continue to skip over the "*". Also, at the end your append will give you a list of letters which you need to use .join() to concatentate.
Try this edit to your code, tested and working:
l = []
def func(s):
inside = False
temp = ""
for i in s:
if i == "*" and not inside:
inside = True
continue
if inside:
if i == "*":
inside = False
continue
else:
temp = i.upper()
else:
temp = i
l.append(temp)
new_string = "".join(l)
print(new_string)
return new_string
func("I want *this text* to be uppercase")

There are several issues here:
You don't return anything inside your function
Your while loop is pointless as i is not incremented inside of it
You need to assign i to i.upper()
You need to convey the input to a list
The corrected code would be as follows:
l = []
def func(s):
inside = False
for i in s:
if i == "*" and not inside:
inside = True
while inside:
if i == "*":
inside = False
else:
i.upper()
if i != "*":
l.append(i)
return l

I would leverage the power of the re module:
import re
st = "I want *this text* to be uppercase and *this one*"
v = re.findall("\*(.*?)\*", st)
for s in v:
st = st.replace(f'*{s}*', s.upper())
print(st)
Output:
>>> I want THIS TEXT to be uppercase and THIS ONE
Anyway, re-editing your code:
def func(s):
l = []
inside = False
for i in s:
if i == "*":
inside = not inside # switch inside on/off when * is found
if inside:
i = i.upper() # upper the character if inside == True
l.append(i)
return l
If you look at your original code, part of the problem is in the following logic:
if i == "*" and not inside:
inside = True # inside is set to True when * is found....
while inside:
if i == "*":
inside = False # and immediately set to False again!

Related

how can i use multiple returned indices in a for loop to update a variable?

My question is: is there any way i can somehow use all the returned capital letter indices and replace them ALL with an underscore? I wished to take the returned values from the uppercase_finder function and insert an underscore in front of those capitalized letters. However, when I run the program, I only get the first capital letter of input with an underscore. Can I somehow iterate all the returned uppercase indices into the part where I insert underscores?
def main():
first_input = input("input here: ")
uppercase_indice = uppercase_finder(first_input)
new_case = first_input[:uppercase_indice] + "_" + first_input[uppercase_indice:]
new_case = new_case.lower()
print(new_case)
def uppercase_finder(x):
for i in range(len(x)):
if x[i].isupper():
return i
main()
Okay so based on the assumption that the overall goal is to print out the string inputted all lowercase and an underscore appended to each letter that was uppercase.
You could iterate through each letter in the string without focusing on the indices at all. Something like:
def main():
first_input = input("input here: ")
updated_input = ""
for letter in first_input:
if(letter.isupper()):
updated_input += "_" + letter.lower()
else:
updated_input += letter
print(updated_input)
Output:
input here: HeLLo
_he_l_lo
Generally though if you want to stick with the uppercase_finder function, the return statement in the loop stops the loop the moment any letter that is uppercase is found. In order to get all of the indices of each letter that is uppercase you would need something like this:
def uppercase_finder(x):
list_of_indices = []
for i in range(len(x)):
if x[i].isupper():
list_of_indices.append(i)
return list_of_indices
Then in the main function you can iterate across the list:
for index in uppercase_indice:
# Make string manipulations for each index
It's obviously an assignment problem so I'm not going to spoon-feed the answer. But I can point out what's the problem in your uppercase_finder.
The problem is that it is returning the index as soon as it find the first upper case. What you can do is
def uppercase_finder(x):
uppercase_indices = []
for i in range(len(x)):
if x[i].isupper():
# Append the index to the list uppercase_indices
return uppercase_indices
There's some problem with your uppercase_finder function, 'return' denotes the end of a function, whenever a return is met, the function will immediately stop and exit with an returned value. For ur case, it seems u wanna return all the indices where there are a capital letter, u may use yield instead of return thus making the function a generator.
def uppercase_finder(x):
for i in range(len(x)):
if x[i].isupper():
yield i
use the output of a generator via a loop:
for capital_pos in uppercase_finder(first_input):
do_sth

Using recursion to concatenate ONLY the letters of an input string into a single output string

"Given a string of both letters and special characters/numbers, use recursion to concatenate the letters into a single string and return it."
My code is below, I'm still learning recursion and am stuck in trying to trace it. I tried a bunch of different lines in this code but idk how to fix what I do have so far:
def decoder(encryptedStr):
if len(encryptedStr) != 0:
if encryptedStr[0].isalpha() == True:
decoded = encryptedStr[0]
decoded.join(decoder(encryptedStr[1:]))
print(decoded)
else:
decoder(encryptedStr[1:])
I haven't had it return anything yet because I'm struggling with the part where I have to join the new letters to the output string. Instead of .join I also tried:
decoded += decoder(encryptedStr[1:])
but it doesn't work bc Nonetype??
Your main issue is that you didnt return, but there are some issues with your approach that make this more complex than need-be.
Think tail-first when doing recursion- What is your end condition, and how do you decide to continue. Typically with this kind of method you do something like, 1) process a single value in the list, 2) let the recursive method handle the rest of it, 3) combine the results.
An easy indicator of the tail-first return here would be to return nothing if the string is empty:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
...
Now in each run we want to operate on one letter and pass the rest to a recursive call. Ignoring the special character requirement, you'd get something like this:
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
return first + rest
Now we can handle the special case where we want to omit letters.
def decoder(encryptedStr):
if len(encryptedStr) == 0:
return ""
first = encryptedStr[0]
rest = decoder(encryptedStr[1:])
if not first.isalpha():
first = ""
return first + rest
And that's all there is to it!
Bonus for some refactoring:
def clean(letter):
return letter if letter.isalpha() else ""
def decoder(encrypted):
if len(encrypted) == 0:
return ""
return clean(encrypted[0]) + decoder(encrypted[1:])
There's a bunch of problems here:
I don't think join does what you want it to do in that case. If you want to add some strings together simply use +=. join would insert decoded character between whatever decoder(encryptedStr[1:]) returns.
You don't have a case for len(encryptedStr) == 0, so it returns default value of None. That's why you cannot append it's results to decoded.
Return immediately if there is nothing to do. Otherwise take the first letter if it matches the condition and add the result of the recursive call (where the parameter is the current encrypted string without the first character).
def decoder(encrypted):
if not encrypted:
return ''
decrypted = encrypted[0] if encrypted[0].isalpha() else ''
return decrypted + decoder(encrypted[1:])
print(decoder('Abc123rtZ5'))
The result is AbcrtZ.
Bonus info (as #JonSG mentioned in the comments):
Run this with print(decoder('A' * 1000)) and you'll see why recursion is a bad idea for this task.
Every recursive function must have a base condition that stops the recursion or else the function calls itself infinitely.
To recursively concatenate ONLY the letters of an input string into a single output string:
some_string = "I2L4o2v3e+P;y|t!o#n"
def decoder(encryptedStr, decoded = ""):
if len(encryptedStr) == 0: # Base condition
return decoded
if encryptedStr[0].isalpha():
decoded += encryptedStr[0]
return decoder(encryptedStr[1:], decoded)
# If the char in the index [0] is not a letter it will be sliced out.
return decoder(encryptedStr[1:], decoded)
print(decoder(some_string))
Output:
ILovePython

How does comparing two chars (within a string) work in Python

I am starting to learn Python and looked at following website: https://www.w3resource.com/python-exercises/string/
I work on #4 which is "Write a Python program to get a string from a given string where all occurrences of its first char have been changed to '$', except the first char itself."
str="restart"
char=str[0]
print(char)
strcpy=str
i=1
for i in range(len(strcpy)):
print(strcpy[i], "\n")
if strcpy[i] is char:
strcpy=strcpy.replace(strcpy[i], '$')
print(strcpy)
I would expect "resta$t" but the actual result is: $esta$t
Thank you for your help!
There are two issues, first, you are not starting iteration where you think you are:
i = 1 # great, i is 1
for i in range(5):
print(i)
0
1
2
3
4
i has been overwritten by the value tracking the loop.
Second, the is does not mean value equivalence. That is reserved for the == operator. Simpler types such as int and str can make it seem like is works in this fashion, but other types do not behave this way:
a, b = 5, 5
a is b
True
a, b = "5", "5"
a is b
True
a==b
True
### This doesn't work
a, b = [], []
a is b
False
a == b
True
As #Kevin pointed out in the comments, 99% of the time, is is not the operator you want.
As far as your code goes, str.replace will replace all instances of the argument supplied with the second arg, unless you give it an optional number of instances to replace. To avoid replacing the first character, grab the first char separately, like val = somestring[0], then replace the rest using a slice, no need for iteration:
somestr = 'restart' # don't use str as a variable name
val = somestr[0] # val is 'r'
# somestr[1:] gives 'estart'
x = somestr[1:].replace(val, '$')
print(val+x)
# resta$t
If you still want to iterate, you can do that over the slice as well:
# collect your letters into a list
letters = []
char = somestr[0]
for letter in somestr[1:]: # No need to track an index here
if letter == char: # don't use is, use == for value comparison
letter = '$' # change letter to a different value if it is equal to char
letters.append(letter)
# Then use join to concatenate back to a string
print(char + ''.join(letters))
# resta$t
There are some need of modification on your code.
Modify your code with as given in below.
strcpy="restart"
i=1
for i in range(len(strcpy)):
strcpy=strcpy.replace(strcpy[0], '$')[:]
print(strcpy)
# $esta$t
Also, the best practice to write code in Python is to use Function. You can modify your code as given below or You can use this function.
def charreplace(s):
return s.replace(s[0],'$')[:]
charreplace("restart")
#'$esta$t'
Hope this helpful.

Is there a way to str1 -= str2?

First of all, I know -= operation doesn't work in str.
But is there a function that works the same?
The reason why I need this is because
def function(self, str_source):
str_source = str_source[:-1] # removing last character of the string
str_source += self.other_function() # adding other characters
return True
In this sort of function, when I do s = s[:-1], the original string does not change.
I know why it does not change, and I know I can just return another modified string, but I am currently working on someone else's code that I can't complete rip up the Project.
So is it possible to remove the substring of a string in a function?
There is no way to change the original string in python. If you want to cuts last characters then prodouce a new string.
For more Details
-= is an assignment operator and assignment operators use for some arithmetic or logical operation.
Here's a hack:
import sys
def function(str_source):
# get existing value
gvars = sys._getframe(1).f_globals
lvars = sys._getframe(1).f_locals
if str_source in lvars:
dir = lvars
elif sttr_source in gvars:
dir = gvars
else:
# might want to raise an exception here
print(str_source, "not found")
return False
ext_str = str(dir[str_source])
ext_str = ext_str[:-1] # removing last character of the string
# Store
dir[str_source] = ext_str
return True
s1 = "hello"
function("s1")
s2 = "world"
function("s2")
print(s1)
print(s2)
Gives:
hell
worl

How to add strings with each other during a loop?

For my programming class, I need to a create a program that takes in a string and two letters as an argument. Whenever the first letter appears in the string, it is replaced with the second letter. I can do this by making the final string into a list. However, our professor has stated that he wants it to be a string, not a list. The code shown below is what I used to make the program work if the final result was to appear in a list.
def str_translate_101(string, x, y):
new_list = []
for i in string:
if i == x:
new_list.append(y)
if i != x:
new_list.append(i)
return new_list
I tried to make one where it would output a string, but it would only return the first letter and the program would stop (which I'm assuming happens because of the "return")
def str_translate_101(string, old, new):
for i in string:
if i == old:
return new
else:
return i
I then tried using the print function, but that didn't help either, as nothing was outputted when I ran the function.
def str_translate_101(string, old, new):
for i in string:
if i == old:
print(new)
else:
print(i)
Any help would be appreciated.
An example of how the result should work when it works is like this:
str_translate_101('abcdcba', 'a', 'x') ---> 'xbcdcbx'
You can use join to merge a list into a string:
def str_translate_101(string, x, y):
new_list = []
for i in string:
if i == x:
new_list.append(y)
else:
new_list.append(i)
return ''.join(new_list)
or use the one-liner
str_tranlsate_101 = str.replace
The simplest solution would be, instead of storing the character in a list you can simply declare an empty string and in the 'if' block append the character to the string using the augmented '+=' operator. E.g.
if i == x:
concat_str += y
As for the return, basically, it will break out of the for loop and return to where the function was called from. This is because it only has 1 objective, which once achieved it will not bother to process any further code and simply go back to where the function was called from.

Categories

Resources