Python: Count the consecutive characters at the beginning of a string - python

I want to count how many leading repeat characters at the beginning of a string. So far the code I wrote:
def count_characters(s, target):
counter = 0
for i in range(len(s)):
if s[i] == target:
counter += 1
else:
return counter
It works. Now I just curious if there is a simpler way to get it done in one or two lines instead of writing an extra function?

If you strip the characters from the beginning, then you are left with a shorter string and can subtract its length from the original, giving you the number of characters removed.
return len(s) - len(s.lstrip(target))
Note: Your shown code will immediately return 0 if the first character does not match target. If you want to check if there is any repeated first character, you don't need to have target and can just use s[0]

You could use next and a range:
return next(i+1 for i in range(len(s)) if s[i] != s[0], len(s))

Related

Encode and Decode a String using the repetition of characters along the string

I've been trying to tackle this problem but I haven't got any luck so far :( so any guidance would be awesome, especially since I'm just learning Python, so the problem is this:
Given a certain string, first you'll have to encode it using the following algorithm:
For every letter, strip the consecutive repetitions, if any, and add a number with the number of times it appears consecutively, including the first time.
After you've completed the encoding you'll have to create a function to decode it using the same criteria/assumptions.
Some examples of how it should work:
“Goooooooooddddd” => “G1o9d5”
“Oooo” => “O1o3”
“anagram” => “a1n1a1g1r1a1m1”
So far I have the following code for the encoding:
def encode(s) :
i = 0
while( i < len(s) - 1) :
count = 1
while s[i] == s[i + 1] :
i += 1
count += 1
if i + 1 == len(s):
break
print(str(s[i]) + str(count),
end = "")
i += 1
print()
# Code for the driver
if __name__ == "__main__" :
encode("Goooooooooddddd")
encode("Oooo")
With this I'm getting the needed output for the encoding part at least.
But I'm unable to actually do the "decoding" part by using this encoding as a starting point.
I know this question is quite silly/basic but I really can't get my head over it
Thanks in advance for the guidance/tips for this challenge problem
Decoding would be very simple in python since it allows character multiplication:
It might be like:
def decode(s):
a = list(s) # separate each character and number
i = 0
z = len(a)
while i < z-1: #This for block checks for any 2 digit numbers
print(i, a[i])
if a[i].isdigit() and a[i+1].isdigit():
a[i] = a[i]+a[i+1]
a.pop(i+1)
print(a)
z-=1
try:
if a[i+2].isdigit():
i-=1
except IndexError:
i-=1
i+=1
final_str = '' # the string that will have the value
for i in range(0, len(a), 2):
final_str += a[i]*int(a[i+1]) #a[i] is the character and int(a[i+1]) is the number of times.
# They are multiplied and then added to the final string
print(final_str) # The final string is printed
Thanks to #Tobi208 for pointing out the bug.
Decoding in pseudo code would involve:
Scan encoded string character by character
If character is alphabetic then add it to the decoded result
Sub scan encoded string for numeric sequence of 1 or more characters till next alphabetic character or end of string.
Repeat the last decoded string character this many times minus one.
Resume from step 1 till encoded string is entirely consumed.

Checks for a Path in a List of Strings Given a Starting and Ending Point

We are tasked to create a program that will check if there is a possible way of going from a starting string to an end string, given a list of strings with the same length. There is a catch, we can only go from the current string to the adjacent string if both strings only have one character that is different. If there is no possible path from the starting string to the end string, just print not possible but if there is, output the number of steps from the start to end.
Example:
li = ["booster", "rooster", "roaster", "coaster", "coasted"]
start = "roaster"
end = "booster"
Output: 3
li = ["booster", "rooster", "roaster", "coastal", "coasted"]
start = "roaster"
end = "coasted"
Output: none
I made an approach which manually checks if adjacent strings only have 1 character differences and returns the result based on these differences. Kinda slow if you ask me, given that the length of the list could be at most 100. Can you demonstrate a faster approach?
def checker(str1, str2, change = 0):
for index, character in enumerate(str1): # Traverses the whole
if character != str2[index]: # string and checks for
change+=1 # character differences
return True if change == 1 else False # Only 1 character is different
li = ["booster", "rooster", "roaster", "coaster", "coasted"]
m = len(li)
for j in range(m): li.append(input())
start, end = input().split()
if end in li: endI = li.index(end) # Gets end string index in the list
else:
print("not possible")
break
if start in li: startI = li.index(start) # Gets start string index in the list
else:
print("not possible")
break
if startI < endI: # If start string comes first before
# the end string, keep incrementing.
while li[startI] != end and startI < m-1:
if not checker(li[startI], li[startI+1]):
print("not possible")
break
startI += 1
print(abs(startI-(li.index(start)+1)))
else: # Otherwise, keep decrementing.
while li[startI] != end and startI > 0:
if not checker(li[startI], li[startI-1]):
print("not possible")
break
startI -= 1
print(abs(startI-(li.index(start)+1)))
If my approach is the fastest (which I highly doubt), I want to know if there are loopholes in my approach. Assume that the start and end strings can also be absent in the list given. Just print not possible if they are not in the list.
I hope that looks better:
import regex
def word_path(words, start, end):
if end in words and start in words:
endI = words.index(end)
startI = words.index(start)
else:
return "not possible"
step = 1 if startI <= endI else -1
for index in range(startI, endI, step):
if not regex.match("(%s){e<=1}" %li[index], li[index + step]):
return "not possible"
return abs(startI - endI) + 1
li = ["booster", "rooster", "roaster", "coastal", "coasted"]
start, end = input().split()
print(word_path(li, start, end))
It is supposed to be regex. Regex provides additional regular expression features, e.g. it makes it possible to check for a number of errors {e<=1}. The re- module doesn't provide these features. I guess that is the reason for the wrong result. You may need to install the regex-module with pip install regex first, but then it should work:
regex_module

Can't figure out what is wrong with the code. Trying an alternative for string.find()

When I try to print the variables indA, indB or string it seems to work. Two letters and a word are inputted, and if the letters are present in the word, the characters between the two letters are printed.
def substring_between_letters(word, start, end):
for index in range(len(word)):
if word[index] == start:
indA = index
break
return indA
if word[index] == end:
indB = index
break
return indB
string = word[indA + 1 : indB]
else:
string = word
return string
This should work:
def substring_between_letters(word, start, end):
hasA = False # Checks if start has already been found
for index in range(len(word)):
if word[index] == start:
hasA = True
aIndex = index
if hasA and word[index] == end:
bIndex = index
return word[aIndex: bIndex + 1]
return word # If substring could not be found
For instance:
>>> print(substring_between_letters("hello everyone!", "l", "e"))
lo e
The problems with your code include:
The break statements cause exiting of the loop before string can be defined. A better way to write this function actually forgoes break statements.
Returning the indA and indB causes the output of the function to just be a single integer.
The else statement is not needed in the for loop. Simply returning word at the end of the function and returning the correct string within the loop is cleaner and computationally faster.
The code as you wrote it has no catch to make sure end appears after start. If the end character appears after the start character, indA will not be defined and you will get an error.
Also, it looks like this code would give you the first instance where s substring begins with the character "start" and ends with the character "end." However, in str.find(sub, start, end), "start" and "end" define a substring of str in which to search for sub. If you were emulating str.find(), you would need four parameters: a string to search from (str), a substring to search for (sub), a starting integer (start) to define where in str to start searching, and an ending integer (end) to define where in str to stop searching.

How to uppercase even letter and lowercase odd letter in a string?

I am creating a function that takes in a string and returns a matching string where every even letter is uppercase and every odd letter is lowercase. The string only contains letters
I tried a for loop that loops through the length of the string with an if statement that checks if the index is even to return an upper letter of that index and if the index is odd to return a lowercase of that index.
def my_func(st):
for index in range(len(st)):
if index % 2 == 0:
return st.upper()
else:
return st.lower()
I expected to have even letters capitalize and odd letter lowercase but I only get uppercase for the whole string.
Some issues in your code:
Currently you are returning from the function on the first index itself, i.e index=0 when you do return st.lower(), so the function will stop executing after it encounters the first index and breaks out of the for loop
Doing st.lower() or st.upper() ends up uppercasing/lowercasing the whole string, instead you want to uppercase/lowercase individual characters
One approach will be to loop over the string, collect all modified characters in a list, convert that list to a string via str.join and then return the result at the end
You also want to refer to each individual characters via the index.
def my_func(st):
res = []
#Iterate over the character
for index in range(len(st)):
if index % 2 == 0:
#Refer to each character via index and append modified character to list
res.append(st[index].upper())
else:
res.append(st[index].lower())
#Join the list into a string and return
return ''.join(res)
You can also iterate over the indexes and character simultaneously using enumerate
def my_func(st):
res = []
#Iterate over the characters
for index, c in enumerate(st):
if index % 2 == 0:
#Refer to each character via index and append modified character to list
res.append(c.upper())
else:
res.append(c.lower())
#Join the list into a string and return
return ''.join(res)
print(my_func('helloworld'))
The output will be
HeLlOwOrLd
You can store your operations beforehand and use them in join with enumerate:
def my_func(st):
operations = (str.lower, str.upper)
return ''.join(operations[i%2](x) for i, x in enumerate(st))
print(my_func('austin'))
# aUsTiN
Tweaking your code a bit, You can use the 'enumerate' and keep appending to the string based on the condition evaluations( for me this was easier since I have been coding in java :))
def myfunc(st):
str=''
for index, l in enumerate(st):
if index % 2 == 0:
str+=l.upper()
else:
str+=l.lower()
return str
def myfunc(str):
rstr = ''
for i in range(len(str) ):
if i % 2 == 0 :
# str[i].upper()
rstr = rstr + str[i].upper()
else:
#str[i].lower()
rstr = rstr + str[i].lower()
return rstr
l = 'YourString'
li=[]
for index,i in enumerate(l):
if index % 2 == 0:
i=i.lower()
li.append(i)
elif index % 2 == 1:
i=i.upper()
li.append(i)
print(''.join(li))
Use this enumerate method to perform your operation
return will terminate your function, so there isn't much point of the loop
st is the whole string, so st.upper() will yield the whole string in upper case.
You can use a bitwise and (&) to check for odd positions. The enumerate function will give you both the positions and the characters so you can easily use it in a list comprehension:
def upLow(s):
return "".join(c.lower() if i&1 else c.upper() for i,c in enumerate(s))
upLow("HelloWorld") # HeLlOwOrLd
The below code should do what you are looking for.
def myfunc(name):
str=""
lc=1;
for character in name:
if(lc%2==0):
str+=character.upper()
else:
str+=character.lower()
lc+=1
return str
def myfunc(a):
newString = ''
for count, ele in enumerate(a, 0):
if count %2 == 0:
newString += (a[count].lower())
else:
newString += ((a[count].upper()))
return newString
You can deconstruct string into collection of characters, apply transformations and reconstruct string back from them
Logic:
First enumerate() over the string to generate key-value pairs of characters and their positions
Then use comprehensions to loop over them and use conditional statement to return odd position characters in lower() case and even position in upper()
Now you have the list ready. Just join() it with an empty string '' to convert the list into a string
Code:
str = 'testing'
''.join([y.upper() if x%2 ==0 else y.lower() for x,y in enumerate(str)])

Count letters in a word in python debug

I am trying to count the number of times 'e' appears in a word.
def has_no_e(word): #counts 'e's in a word
letters = len(word)
count = 0
while letters >= 0:
if word[letters-1] == 'e':
count = count + 1
letters = letters - 1
print count
It seems to work fine except when the word ends with an 'e'. It will count that 'e' twice. I have no idea why. Any help?
I know my code may be sloppy, I'm a beginner! I'm just trying to figure out the logic behind what's happening.
>>> word = 'eeeooooohoooooeee'
>>> word.count('e')
6
Why not this?
As others mention, you can implement the test with a simple word.count('e'). Unless you're doing this as a simple exercise, this is far better than trying to reinvent the wheel.
The problem with your code is that it counts the last character twice because you are testing index -1 at the end, which in Python returns the last character in the string. Fix it by changing while letters >= 0 to while letters > 0.
There are other ways you can tidy up your code (assuming this is an exercise in learning):
Python provides a nice way of iterating over a string using a for loop. This is far more concise and easier to read than using a while loop and maintaining your own counter variable. As you've already seen here, adding complexity results in bugs. Keep it simple.
Most languages provide a += operator, which for integers adds the amount to a variable. It's more concise than count = count + 1.
Use a parameter to define which character you're counting to make it more flexible. Define a default argument for using char='e' in the parameter list when you have an obvious default.
Choose a more appropriate name for the function. The name has_no_e() makes the reader think the code checks to see if the code has no e, but what it actually does is counts the occurrences of e.
Putting this all together we get:
def count_letter(word, char='e'):
count = 0
for c in word:
if c == char:
count += 1
return count
Some tests:
>>> count_letter('tee')
2
>>> count_letter('tee', 't')
1
>>> count_letter('tee', 'f')
0
>>> count_letter('wh' + 'e'*100)
100
Why not simply
def has_no_e(word):
return sum(1 for letter in word if letter=="e")
The problem is that the last value of 'letters' in your iteration is '0', and when this happens you look at:
word[letters-1]
meaning, you look at word[-1], which in python means "last letter of the word".
so you're actually counting correctly, and adding a "bonus" one if the last letter is 'e'.
It will count it twice when ending with an e because you decrement letters one time too many (because you loop while letters >= 0 and you should be looping while letters > 0). When letters reaches zero you check word[letters-1] == word[-1] which corresponds to the last character in the word.
Many of these suggested solutions will work fine.
Know that, in Python, list[-1] will return the last element of the list.
So, in your original code, when you were referencing word[letters-1] in a while loop constrained by letters >= 0, you would count the 'e' on the end of the word twice (once when letters was the length-1 and a second time when letters was 0).
For example, if my word was "Pete" your code trace would look like this (if you printed out word[letter] each loop.
e (for word[3])
t (for word[2])
e (for word[1])
P (for word[0])
e (for word[-1])
Hope this helps to clear things up and to reveal an interesting little quirk about Python.
#marcog makes some excellent points;
in the meantime, you can do simple debugging by inserting print statements -
def has_no_e(word):
letters = len(word)
count = 0
while letters >= 0:
ch = word[letters-1] # what is it looking at?
if ch == 'e':
count = count + 1
print('{0} <-'.format(ch))
else:
print('{0}'.format(ch))
letters = letters - 1
print count
then
has_no_e('tease')
returns
e <-
s
a
e <-
t
e <-
3
from which you can see that
you are going through the string in reverse order
it is correctly recognizing e's
you are 'wrapping around' to the end of the string - hence the extra e if your string ends in one
If what you really want is 'has_no_e' then the following may be more appropriate than counting 'e's and then later checking for zero,
def has_no_e(word):
return 'e' not in word
>>> has_no_e('Adrian')
True
>>> has_no_e('test')
False
>>> has_no_e('NYSE')
True
If you want to check there are no 'E's either,
def has_no_e(word):
return 'e' not in word.lower()
>>> has_no_e('NYSE')
False
You don't have to use a while-loop. Strings can be used for-loops in Python.
def has_no_e(word):
count = 0
for letter in word:
if letter == "e":
count += 1
print count
or something simpler:
def has_no_e(word):
return sum(1 for letter in word if letter=="e")

Categories