2

I am very new to programming (6 weeks, self taught on the net with "codecademy" and "python the hard way"). I decided it was time I started experimenting at writing code without direction and I hit a wall with my second project.

I am trying to make a "secret coder" that takes a raw_input string and replaces every letters in it with the next one in the alphabet. With my very limited knowledge, I figured that a dictionary should be the way to go. With a "little" googling help, I wrote this:

alpha = {"a" : "b", "b" : "c", "c" : "d", "d" : "e", "e" : "f",  "f" : "g","g" : "h"
         , "h" : "i", "i" : "j", "j" : "k", "k" : "l", "l" : "m","m" : "n", "n" : "o"
         , "o" : "p", "p" : "q", "q" : "r", "r" : "s","s" : "t", "t" : "u", "u" : "v"
         , "v" : "w", "w" : "x", "x" : "y", "y" : "z", "z" : "a"}

entry = raw_input("Please write a sentence you want to encode: ")

def encode(entry, letters):
    for k, v in letters.iteritems():
        if k in alpha:
            entry = entry.replace(k, v)
    return entry
print encode(entry, alpha)

The problem that I have is that only half the letters in my string are replaced by the correct values from the dictionary. "a" and "b" will both be printed as "c" when "a" should be printed as "b" and "b" should be printed as "c" and so on.

illustration

Where I get completely lost is that, when I replaced every value in my dictionary with numbers, it worked perfectly.

illustration bis

That's the gist of it, I really don't get what is wrong with my code.

Thank you in advance for your help.

PS: This was my very first post on stackoverflow, hopefully I did everything the way I should.

EDIT: Since I cannot give reputation yet, I will just thank you all here for your helpful answers. I can see a bit clearer now where my mistake is and I will take the info provided here to fix my code and work on understanding it properly. Also I can see that there are much more logical and straightforward approches to solving this kind of problem. Functions are still a bit blurry to me but I guess it is normal so early on.

Gorian
  • 21
  • 1
  • 3
  • you need to loop on entry, not letters, and join each char back with '' – YOU Aug 19 '16 at 02:05
  • 1
    Imagine this scenario... you have an `a` in your string, and you loop and change all the `a`'s to `b`s, but then, you loop over and change all the `b`s to `c`s (which can include the `a`s you've changed to `b`s etc...) – Jon Clements Aug 19 '16 at 02:08
  • :D Dude i like you enthusiasm. Python is convenient yet powerful, so similar excercise would be to encrypt your messages using actual crypto solutions - see for yourself how simple it gets with Python http://stackoverflow.com/questions/30056762/rsa-encryption-and-decryption-in-python :) – Miro Rodozov Aug 19 '16 at 02:38
  • As many others told, you can just iterate as in `for e in entry: result += letters[e]...`and python will do the rest of the work for you. Something it is worth for you to learn (the sooner the better) is you should not modify the object where you're iterating over, this will save you tons of headaches in the future. –  Aug 19 '16 at 02:39

5 Answers5

1

The problem is that since you're iterating over the dictionary instead of the string you might replace original character many times. For example with given input of 'ab' the first replace will result to 'bb' and second to 'cc' in case that iteritems returns the keys in order a, b. Note that since dictionary is unordered collection the order of returned items is random.

You could fix the problem by using generator expression to iterate over the source string and join to create the result:

def encode(entry, letters):
    return ''.join(letters.get(c, c) for c in entry)

The above example is calling get instead of using index operator to handle the cases where alpha doesn't contain a letter in source string. The difference between get and index operator is that get takes second argument that is default value which will be returned in case that key doesn't exist. In the above example the default value is the character itself.

niemmi
  • 17,113
  • 7
  • 35
  • 42
  • Pretty clean solution (I upvoted it) since the OP is a beginner, it would be cool to provide a broader explanation. Like the get() method and how it's picking the value from letters or using the provided key as default in case the item is not found. –  Aug 19 '16 at 02:54
1

Xetnus is correct you need to loop through entry variable instead of the letters. maybe something like this

def encode(entry, letters):
    new_entry = ''
    for letter in entry:
        if letter in letters:
            new_entry += letters[letter]
    return new_entry
print encode(entry, alpha)
davidejones
  • 1,869
  • 1
  • 16
  • 18
0
def encode(entry, letters):
    for k, v in letters.iteritems():
        if k in alpha:
            entry = entry.replace(k, v)
    return entry
print encode(entry, alpha)

Calling encode(entry, alpha) in your print function, you pass the encode() function the alpha dictionary to be the letters variable in the function. Then, you use the iterator to loop through the letters variable (which is the alpha dictionary). Then your if statement checks to see if that k (which you just got from the letters variable, which is the alpha dictionary) is in the alpha dictionary, which of course it is.

You'll need to do something like this instead:

for k, v in letters.iteritems():
    if k in entry:
        entry = entry.replace(k, v)
Xetnus
  • 353
  • 2
  • 4
  • 13
0

Dictionaries aren't ordered, so you are replacing some letters ('a' -> 'b') and others are getting skipped over.

For your edification what you are doing is called a rot (rotation). The most well-known one is rot13.

Writing things yourself is helpful when you first start out, but there is a pretty cool standard library function called string.maketrans which will do exactly this!

dreamriver
  • 1,291
  • 2
  • 15
  • 20
0

Others have pointed out how to do this correctly using a dictionary as lookup. However, you can also use the str.translate method which avoids having to do the look up and rebuilding a string.

import string

trans = string.maketrans(
    'abcdefghijklmnopqrstuvwxyz', # from...
    'bcdefghijklmnopqrstuvwxyza' # to
)

print raw_input('Please enter something to encode: ').translate(trans)
Jon Clements
  • 138,671
  • 33
  • 247
  • 280
  • This is a very elegant and simple way of doing what I wanted to do and I will definitely use it as I already see how I can make my code do more complex manipulations of strings with it. I will work on making my function do what I wanted with the help provided in other answers, then, once I will be able to code it properly, I will switch to using the str.translate method because of it's simplicity. Thanks for suggesting it. – Gorian Aug 19 '16 at 15:14