Replace multiple characters in a string with one from a generator - python

I'm looking for a way to take a string that looks like the following:
(a,1),(b,1),(a,1),(b,5),(a,1),(b,2),(a,1),(b,1),(a,2),(b,6),(a,2)
And replace the first "a" with an even number, the second with the next
up even number, and so on for however long the string is. Then I'd like to take the first "b" and assign it an odd number, then the next "b" gets the
next highest odd number, and so on for however long the string is. I'm
working primarily in Python 2.7, but would be willing to look into other languages if a solution exists in that.

The following regular expression substitution should work:
import re
def odd_even(x):
global a,b
if x.group(1) == 'a':
a += 2
return str(a)
else:
b += 2
return str(b)
a = 0
b = -1
source = "(a,1),(b,1),(a,1),(b,5),(a,1),(b,2),(a,1),(b,1),(a,2),(b,6),(a,2)"
print re.sub("([ab])", odd_even, source)
This prints:
(2,1),(1,1),(4,1),(3,5),(6,1),(5,2),(8,1),(7,1),(10,2),(9,6),(12,2)

even = 2
while "a" in string:
string = string.replace("a", str(even), 1)
even += 2
odd = 1
while "b" in string:
string = string.replace("b", str(odd), 1)
odd += 2

Related

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.

trying to find if a character appears successively in a string

Simple script to find if the second arguement appears 3 times successively in the first arguement. I am able to find if the second arguement is in first and how many time etc but how do i see if its present 3 times successively or not ?
#!/usr/bin/python
import string
def three_consec(s1,s2) :
for i in s1 :
total = s1.count(s2)
if total > 2:
return "True"
print three_consec("ABABA","A")
total = s1.count(s2) will give you the number of s2 occurrences in s1 regardless of your position i.
Instead, just iterate through the string, and keep counting as you see characters s2:
def three_consec (string, character):
found = 0
for c in string:
if c == character:
found += 1
else:
found = 0
if found > 2:
return True
return False
Alternatively, you could also do it the other way around, and just look if “three times the character” appears in the string:
def three_consec (string, character):
return (character * 3) in string
This uses the feature that you can multiplicate a string by a number to repeat that string (e.g. 'A' * 3 will give you 'AAA') and that the in operator can be used to check whether a substring exists in a string.

Writing a recursive function

I am trying to write a recursive function in Python to count the number of pairs of repeated characters in a string. Example: "hellmoo" = 2
To get me started, I first tried to write an iterative version of the program. Here's my attempt:
counter = 0
string = input("Enter string:" )
for i in range(len(string)-1):
if string[i] == string[i+1]:
counter = counter+1
print (counter)
Now, I do not understand how I can write a recursive function from the above iterative program. I have tried to think of my base case as:
if string == "":
return (0)
I'm not sure if I am correct there. Could someone please help me do this? Thanks!
your logic assuming two characters in the string, so I think you need two base cases, for empty string and for one character string. (or one base case for string shorter then 2)
maybe something like this:
def count_doubles(string):
if len(string) < 2:
return 0
else:
if string[0] == string[1]:
return count_doubles(string[1:]) + 1
else:
return count_doubles(string[1:])
>>> count_doubles("hellmoo")
2
p.s.
I don't know why you want to do this with recursion, I dont think it's good idea for this task.
a more pythonic way to do it can be:
>>> string = "hellmoo"
>>> len(filter(lambda x:x[0]==x[1],zip(string[1:],string[:-1])))
2
This is kind of a silly assignment for Python, but in other languages that use a different fundamental data type, like a cons-list instead of an array, it makes sense.
You know how to write counter for a 2-element string.
For a 3-element string, you do the 2-element count on s[:2], then you do the 2-element count on s[1:].
For a 4-element string, you do the 2-element count on s[:2], then you do the 3-element count on s[1:].
For an N-element string, you do the 2-element count on s[:2], then you do the N-1-element count on s[1:].
Of course that leaves out the 0-element and 1-element cases, but you can just add 2 more base cases for that.
Defining a basic recursion with a wrapper:
def recursion(str, idx):
if (idx + 1 == len(str)):
return 0
if (str[idx+1] == str[idx]):
return 1 + recursion(str, idx + 1)
return recursion(str, idx + 1)
def count(str):
if len(str) == 0:
return 0
return recursion(str, 0)
>>> count("hellmoo")
2
>>> count("hellbb")
2
>>> count("hellbbvv")
3
>>> count("hh")
1
>>> count("")
0

Replace numbers in string by respective result of a substraction

I have a string like this:
"foo 15 bar -2hello 4 asdf+2"
I'd like to get:
"foo 14 bar -3hello 3 asdf+1"
I would like to replace every number (sequence of digits as signed base-10 integers) with the result of a subtraction executed on each of them, one for each number.
I've written a ~50 LOC function that iterates on characters, separating signs, digits and other text, applying the function and recombining the parts. Although it has one issue my intent with the question is not to review it. Instead I'm trying to ask, what is the pythonic way to solve this, is there an easier way?
For reference, here is my function with the known issue, but my intention is not asking for a review but finding the most pythonic way instead.
edit to answer the wise comment of Janne Karila:
preferred: retain sign if given: +2 should become +1
preferred: zero has no sign: +1 should become 0
preferred: no spaces: asdf - 4 becomes asdf - 3
required: only one sign: -+-2 becomes -+-3
edit on popular demand here is my buggy code :)
DISCLAIMER: Please note I'm not interested in fixing this code. I'm asking if there is a better approach than something like mine.
def apply_to_digits(some_str,handler):
sign = "+"
started = 0
number = []
tmp = []
result = []
for idx,char in enumerate(some_str):
if started:
if not char.isdigit():
if number:
ss = sign + "".join(number)
rewritten = str(handler(int(ss)))
result.append(rewritten)
elif tmp:
result.append("".join(tmp))
number = []
tmp = []
sign = "+"
started = 0
# char will be dealt later
else:
number.append(char)
continue
if char in "-+":
sign = char
started = 1
if tmp:
result.append("".join(tmp))
tmp = []
tmp.append(char)
continue
elif char.isdigit():
started = 1
if tmp:
result.append("".join(tmp))
tmp = []
number.append(char)
else:
tmp.append(char)
if number:
ss = sign + "".join(number)
rewritten = str(handler(int(ss)))
result.append(rewritten)
if tmp:
result.append("".join(tmp)), tmp
return "".join(result)
#
DISCLAIMER: Please note I'm not interested in fixing this code. I'm asking if there is a better approach than something like mine.
You could try using regex, and using re.sub:
>>> pattern = "(-?\d+)|(\+1)"
>>> def sub_one(match):
return str(int(match.group(0)) - 1)
>>> text = "foo 15 bar -2hello 4 asdf+2"
>>> re.sub(pattern, sub_one, text)
'foo 14 bar -3hello 3 asdf+1'
The regex (-?\d+)|(\+1) will either capture an optional - sign and one or more digits, OR the literal sequence +1. That way, the regex will make sure that all of your requirements when converting digits work properly.
The regex (-?\d+) by itself does the right thing most of the time, but the (\+1) exists to make sure that the string +1 always converts to zero, without a sign. If you change your mind, and want +1 to convert to +0, then you can just use only the first part of the regex: (-?d+).
You could probably compress this all into a one-liner if you wanted:
def replace_digits(text):
return re.sub("(-?\d+)|(\+1)", lambda m: str(int(m.group(0)) - 1), text)

How to count how many "ZE"s in a given string

I am trying to complete this attempt to count how many times "Z" eats "E" in the string. In other words I need to count how many times "ZE" is in the given string:
letters = "GOLDZEZEDUZZEOZBIX"
Why is this code only returning 1?
def is_eaten(data):
count = 0
if "Z" and "E" in data:
count += 1
return count
You can simply use the count method:
letters.count('ZE') # returns 3
Because you set count to one if Z and E is in data. Not once for every ZE, but if.
Read up on for loops.
Why is this code only returning 1?
Multiple reasons.
First, there's no loop in the code, so there's no way it could ever return more than 1.
Second, if "Z" and "E" in data doesn't mean what you think. It's true if "Z" is true, and also "E" in data is true. In other words, it's equivalent to if ("Z") and ("E" in data). But, even if the parentheses went the other way, and it were if ("Z" and "E") in data) it wouldn't work. Since "Z" and "E" is just "E" (try it and see), that would just be checking whether "E" appears in data.
You need if "Z" in data and "E" in data to do what you're trying to do.
Third, even if you fix that, the logic doesn't make any sense. This is true if there's any "Z" anywhere, and any "E" anywhere. So, it would be true for "EZ", which is obviously wrong. You only want it to be true if the substring "ZE" appears in the data, right? You can use if "ZE" in data to mean exactly that. Or, if you're not allowed to do that for some reason, first find a "Z" and check whether the next character is "E".
Here's a way you could do this (not a very good way, but the closest I could come up with to what you tried):
def count_eaten(data):
count = 0
while True:
index = data.find('Z')
if index == -1:
return count
data = data[index+1:]
if data[0] == 'E':
count += 1
Or, more simply:
def count_eaten(data):
count = 0
while True:
index = data.find('ZE')
if index == -1:
return count
data = data[index+1:]
count += 1
Or, even more simply:
def count_eaten(data):
return data.count('ZE')
I'm guessing your professor doesn't want this last one, and probably doesn't want the one before it either… but that's really just a guess (as is the fact that this is homework in the first place).
for even another solution try this:
def keyCount(dataset, key):
return dataset.count(key)
correct usage of this method would then look like:
>>> letters = "GOLDZEZEDUZZEOZBIX"
>>> key = "ZE"
>>> keyCount(letters, key)
3
or
>>> keyCount(letters, "ZE")
3
or
>>> keyCount("GOLDZEZEDUZZEOZBIX", "ZE")
3
etc..

Categories

Resources