Vito
Vito

Reputation: 33

Change each character in string to the next character in alphabet

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

Answers (8)

Tomerikoo
Tomerikoo

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

nidhi bhatnagar
nidhi bhatnagar

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

Georgy
Georgy

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

roganjosh
roganjosh

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

Mohamed Abu ElGheit
Mohamed Abu ElGheit

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

dawg
dawg

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

Merlin
Merlin

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

niemmi
niemmi

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

Related Questions