1

I am writing a cypher and I've assigned numerical values to every letter in the alphabet, alphabet_dictionary = {'a': 0, 'b': 1, 'c': 2,... etc.}.

I first convert the letter in message to numbers, do some arithmetic and store those numbers in a variable named code.

I want to convert them back to letters.

So far I have:

for number in code:
    for letter, value in alphabet_dictionary.items():
        if value == number:
            coded_message.append(letter)

This works. I end up with a the correct coded message inside my coded_message list. Is there is something better or more efficient?

Why can't I do that in a single line? I first tried using:

for number in code:
    coded_message.append(letter for letter, value in alphabet_dictionary.items() if value == number)

but that only appends the memory allocation address for the individual characters for example:

`[<generator object <genexpr> at 0x1011bb240>, <generator object <genexpr> at 0x1011bb1f8>, <generator object <genexpr> at 0x1011bb288>, <generator object <genexpr> at 0x1011bb2d0>, <generator object <genexpr> at 0x1011bb318>, <generator object <genexpr> at 0x1011bb360>, <generator object <genexpr> at 0x1011bb3a8>]'

Why is this happening?

Community
  • 1
  • 1
lokilindo
  • 682
  • 5
  • 17
  • 1
    Possible duplicate of [Inverse dictionary lookup - Python](http://stackoverflow.com/questions/2568673/inverse-dictionary-lookup-python) – Tadhg McDonald-Jensen Mar 31 '16 at 05:36
  • if you are absolutely certain that there will be exactly one match to the value you can use `coded_message.append(next(letter for letter, value in alphabet_dictionary.items() if value == number))` to get the next (first) value of the generator expression but as the accepted answer of [Inverse dictionary lookup - Python](http://stackoverflow.com/questions/2568673/inverse-dictionary-lookup-python) states: there is no inherit guarantee that the value is present in the dictionary. – Tadhg McDonald-Jensen Mar 31 '16 at 05:38
  • since you are simply linking letters to numbers you could just use use `string.ascii_lowercase[index]` to look up a letter by index and `string.ascii_lowercase.index(char)` to get the index of a character. – Tadhg McDonald-Jensen Mar 31 '16 at 05:41

2 Answers2

1

You're not getting the memory locations for the letter, you're getting the memory locations for what it thinks are generators you're (inadvertently) creating. .append() takes a single argument, and wheny ou give it a generator-expr, it happily treats that as the argument, and adds it. You can either wrap the gen-expr in next as @Tadhg McDonald-Jensen mentions, or you can try changing .append() to .extend()

To elaborate a little:

x = letter for letter, value in alphabet_dictionary.items() if value == number
type(x)  ## generator-expression
for i in x: 
    print(i)
## will only print the single value that satisfied this generator

Similarly, if you made it an explicit list comprehension inside the .append(), it might more readily show the issue:

for number in code:
    coded_message.append([letter for letter, value in alphabet_dictionary.items() if value == number])

coded_message
## [["a"], ["b"],... ] - not the right order, of course, but now you have a list of lists, which is probably not what you want.

If you use either next or extend, you'll get a list of letters, which is probably what you want.

In all likelihood though, if you want the reverse-mapping, I would build the reverse-mapping as a dictionary as well. When you first create your mapping of, example, "a" = 0, also make a dictionary with 0 = "a". This will be faster for look-up (though with an alphabet-sized list, you'd probably never notice), and will let you do sanity checking, such as

for letter, number in alphabet_dict.items():
    if number_dict[number] != letter:
        raise "Something got confused: got {}->{}->{}".format(letter, number, number_dict[number])
dwanderson
  • 2,775
  • 2
  • 25
  • 40
0
import string
#this builds a similar dictionary to yours:
alphabet_dictionary = {a:ord(a)-ord('a') for a in string.ascii_lowercase} 

You could use map:

code = 'somestring'
coded_message = map(alphabet_dictionary.__getitem__, code)

For decoding:

#invert the dictionary, in your case it's easy because it's 1-to-1:
decode_dictionary = {v:k for k,v in alphabet_dictionary.items()}
decoded_message = map(decode_dictionary.__getitem__, coded_message)
arianvc
  • 101
  • 10