Reputation: 33
I am coding in Python 2.7 using PyCharm on Ubuntu.
I am trying to create a function that will take a string and change each character to the character that would be next in the alphabet.
def LetterChanges(str):
# code goes here
import string
ab_st = list(string.lowercase)
str = list(str)
new_word = []
for letter in range(len(str)):
if letter == "z":
new_word.append("a")
else:
new_word.append(ab_st[str.index(letter) + 1])
new_word = "".join(new_word)
return new_word
# keep this function call here
print LetterChanges(raw_input())
When I run the code I get the following error:
/usr/bin/python2.7 /home/vito/PycharmProjects/untitled1/test.py
test
Traceback (most recent call last):
File "/home/vito/PycharmProjects/untitled1/test.py", line 17, in <module>
print LetterChanges(raw_input())
File "/home/vito/PycharmProjects/untitled1/test.py", line 11, in LetterChanges
new_word.append(ab_st[str.index(letter) + 1])
ValueError: 0 is not in list
Process finished with exit code 1
What am I doing wroing in line 11? How can I get the following character in the alphabet for each character and append it to the new list?
Many thanks.
Upvotes: 2
Views: 37216
Reputation: 19422
This can be done much easier and efficiently by using a look-up instead of using index
for each letter.
look_up = dict(zip(string.ascii_lowercase, string.ascii_lowercase[1:]+'a'))
The zip
above creates tuples of the form ('a', 'b')...
including ('z', 'a')
. Feeding that into the dict
constructor makes a dict of the same form.
So now the code can be simply:
def LetterChanges(s):
from string import ascii_lowercase
look_up = dict(zip(ascii_lowercase, ascii_lowercase[1:]+'a'))
new_s = ''
for letter in s:
new_s += look_up[letter]
return new_s
Even this dict creation and the loop can be saved by using the str.maketrans
method and feeding its result to str.translate
:
def LetterChanges(s):
from string import ascii_lowercase
return s.translate(str.maketrans(ascii_lowercase, ascii_lowercase[1:]+'a'))
Upvotes: 0
Reputation: 25
The problem you are solving is of Ceaser cipher. you can implement the formula in your code.
E(x) = (x+n)%26 where x is your text and n will be the shift.
Below is my code. (I write the code in python 3)
import ast
n = ast.literal_eval(input())
n1 = n[0]
step = n[1]
def enc_dec(string,step):
result = ''
for i in string:
temp = ''
if i=='':
result = result+i
elif i.isupper():
temp = chr((ord(i) + step - 65) % 26 + 65)
else:
temp = chr((ord(i) + step - 97) % 26 + 97)
result = result + temp
return result
print(enc_dec(n1,step))
Upvotes: 1
Reputation: 13717
Here is a more general approach where user can choose how many characters back or forth they want to shift the string, and which alphabets they want to use:
from string import (ascii_lowercase,
ascii_uppercase)
from typing import Sequence
def shift(string: str,
*,
alphabets: Sequence[str] = (ascii_lowercase, ascii_uppercase),
step: int = 1) -> str:
"""Shifts `string` by `step` in `alphabets`"""
def shift_char(char):
for alphabet in alphabets:
try:
return alphabet[(alphabet.index(char) + step) % len(alphabet)]
except ValueError:
pass
return char
return ''.join(map(shift_char, string))
Examples of usage:
# default parameters
>>> shift('abcxyz, ABCXYZ')
'bcdyza, BCDYZA'
# negative shift
>>> shift('abcxyz, ABCXYZ',
step=-1)
'zabwxy, ZABWXY'
# using other alphabets
>>> russian_alphabet = 'абвгдеёжзийклмнопрстуфхцчшщъыьэюя'
>>> shift('уегнп гбзмюсзм',
alphabets=[russian_alphabet])
'фёдор двинятин'
Note on performance:
Note that alphabet[(alphabet.index(char) + step) % len(alphabet)]
is O(n) due to searching of an index of an element in a string. While for small strings it's ok, for large strings it would make sense to have a dictionary mapping each character in an alphabet to its index, like:
mapping = dict(map(reversed, enumerate(alphabet)))
Upvotes: 0
Reputation: 13185
Two main things. 1) Don't use the Python built-in str
to define variables as it could lead to unusual behaviour. 2) for letter in range(len(str))
does not return a letter at all (hence the error stating that 0 is not in your list). Instead, it returns numbers one by one up to the length of str
. Instead, you can just use for letter in my_string
.
EDIT: Note that you don't need to convert the string into a list of letters. Python will automatically break the string into individual letters in for letter in strng
. Updated answer based on comment from linus.
def LetterChanges(strng):
ab_st = list(string.lowercase)
output_string = []
for letter in strng:
if letter == 'z':
output_string.append('a')
else:
letter_index = ab_st.index(letter) + 1
output_string.append(ab_st[letter_index])
new_word = "".join(output_string)
return new_word
# keep this function call here
print LetterChanges(raw_input())
Upvotes: 2
Reputation: 59
this is my code i think it is very simple
def LetterChanges(st):
index = 0
new_word = ""
alphapet = "abcdefghijklmnopqrstuvwxyzacd"
for i in st.lower():
if i.islower(): #check if i s letter
index = alphapet.index(i) + 1 #get the index of the following letter
new_word += alphapet[index]
else: #if not letter
new_word += i
return new_word
print LetterChanges(raw_input())
Upvotes: 1
Reputation: 104005
I think you are making this too complicated.
Just use modulo to roll around to the beginning of the string:
from string import ascii_letters
s='abcxyz ABCXYZ'
ns=''
for c in s:
if c in ascii_letters:
ns=ns+ascii_letters[(ascii_letters.index(c)+1)%len(ascii_letters)]
else:
ns+=c
Which you can reduce to a single unreadable line if you wish:
''.join([ascii_letters[(ascii_letters.index(c)+1)%len(ascii_letters)]
if c in ascii_letters else c for c in s])
Either case,
Turns abcxyz ABCXYZ
into bcdyzA BCDYZa
If you want it to be limited to upper of lower case letters, just change the import:
from string import ascii_lowercase as letters
s='abcxyz'
ns=''
for c in s:
if c in letters:
ns=ns+letters[(letters.index(c)+1)%len(letters)]
else:
ns+=c
Upvotes: 7
Reputation: 25659
Look at this for ideas to simplify your code.
def LetterChanges(word):
zabc = 'abcdefghijklmonpqrstuvwxyzabc'
ab_st = list(zabc)
new_word = []
for letter in list(word.lower().strip()):
new_word.append(ab_st[zabc.index(letter) + 1])
new_word = "".join(new_word)
return new_word
LetterChanges(" Chicago ")
Upvotes: 0
Reputation: 17263
There's two issues with the code. Instead of looping letters you're looping over numbers since you're calling range(len(str))
. The second issue is that within the loop you assign a string to new_word
which will cause the next iteration to fail since string
doesn't have method append
. If you make the following changes it should work:
for letter in str: # for letter in range(len(str)):
if letter == "z":
new_word.append("a")
else:
new_word.append(ab_st[str.index(letter) + 1])
# new_word = "".join(new_word)
new_word = "".join(new_word)
Upvotes: 0