Getting the value from an unknown, function defined, key

I have looked under several sources and either their explanation is too complex for me to understand or they are solving a different problem.

I need the code to iterate through the characters in a string, ex “what are you doing later today” and return the corresponding number of the alphabet.

I immediately thought Dictionary! and built one…

…and I made a “for” loop to iterate through each letter of the string…

… but I cannot for the life of me get the results of the iteration to properly “get()” the value of the iteration from my dictionary.

Again, using code wars which has a test built into it which I don’t understand how to duplicate in PyCharm to test each section as I go for errors.

def alphabet_position(text):
    alphabet = {
        'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7,
        'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14,
        'o': 15, 'p': 16, 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21,
        'v': 22, 'w': 23, 'x': 24, 'y': 25, 'z': 26
    }
    for letter in text:
        if letter in alphabet:
            return alphabet.get(letter)
        else:
            continue

the test code that is showing on the site is:

from random import randint
test.assert_equals(alphabet_position(“The sunset sets at twelve o’ clock.”), “20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11”)
test.assert_equals(alphabet_position(“The narwhal bacons at midnight.”), “20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20”)

number_test = “”
for item in range(10):
number_test += str(randint(1, 9))
test.assert_equals(alphabet_position(number_test), “”)

Where/how do I plug in this test code?

If I replace the function with “phrase = ‘what’s up with you tonight’” it pulls the value from the dictionary just fine.

I would recommend against trying to run codewars’ test case locally at this stage in your learning process. Instead, look at what the test expects your function to output, and try to replicate that locally, before running the test.

It is possible to run the test locally by installing codewars’ test framework for python, but again, it is unnecessary at this point. Focus on getting your code to do what you want first.

So, with that in mind, what does codewars expect from you? Let’s take a look:

Input: "The sunset sets at twelve o' clock."

Expected output: "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11"

It wants a string of numbers, separated by spaces. Now, what does your code do?

Input: "The sunset sets at twelve o' clock."
Ouput: 8

It outputs a single number. And the number is 8, which corresponds to the letter ‘h’, not 20 for ‘T’, which may also surprise you. Why does this happen?

Because you return as soon as you encounter a (lowercase) letter, the first of which is the ‘h’ in the word ‘The’ from the input string. When a function returns, it’s finished and won’t do anything else.

What you should do instead is collect the numbers in some way, and only return when the entire input string has been processed. See if you can get further with these hints.

Some other comments on your code:

While using get to get values from dictionaries is perfectly functional, it is more pythonic to use subscripts. Instead of alphabet.get(letter), use alphabet[letter].

The continue keyword causes a loop to skip to the next iteration immediately, but you have put a continue at the end of a loop. In that position, it serves no purpose since the loop would normally go to the next iteration anyway.

Manually building such a long dictionary is error prone. Python is batteries included language so one can take advantage of constants defined in built-in string module, count iterator from built-in itertools which returns evenly spaced values and feed them to built-in zip and dict:

from itertools import count
from string import ascii_lowercase as letters

alphabet = dict(zip(letters, count(1)))

Alternatively you don’t need to build dictionary, just access index + 1 of a character:

from string import ascii_lowercase as letters

print(letters.index('c') + 1)

Hi Brad
Here is my anotated solution to your problem. I’ve tried to make it as simple as I can.

import string                               # import common strimg functions
print(string.ascii_lowercase)               # lowercase letters
a = '1abc def!xyzabc'                       # string to test
for c in a:                                 # loop over characters in string
    if c.isalpha():                         # check if character is in alphabet, skip to next character if not
        c = c.lower()                       # make it lower case
        i = string.ascii_lowercase.find(c)  # find index of c in lowercas alpha string.
        print(i + 1 , end = ' ')            # print result, end of output is ' ' rather than '\n' which is the default.
print()                                     # print empty line to add a '\n' to output

To find out more look up:

  1. The string module in the standard library, this includes some usefull constants.
  2. the string built-in types. You can loop over characters in a string, get infoormation about them and convert them
  3. .find methods for string objects.
  4. print, which lets you have contro over your output.
    I could write this in a much terser format but it would be harder to understand. Hope thiis helps.

Another solution: import letters constant from string module, make text to be converted into lowercase, if character from text is in letters find it’s index and add 1 and convert to str, then join stream of strings into new str using space:

from string import ascii_lowercase as letters

message = "The sunset sets at twelve o' clock."

coded = ' '.join(str(letters.index(char) + 1) for char in message.lower() if char in letters)

assert coded == "20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11"

OK so a few things:
First, I need to take whatever the string is and make the whole thing lower(), then iterate through it. Second, storing the results: I’m thinking this can be done by establishing a new variable for the results of ‘letter in text’, say ‘lower_text’. I now have the question of whether I need to get the value for each lower case letter as it iterates or if I can pull them all at once from the new storage variable I created.
Third, I need to figure out how to push the go button on the syntax without using the word return until later. I’m not 100% sure my brain understands this yet as I’m thinking of “print” and “return” as some sort of action command that are needed for your syntax to actually do anything.
I have seen something like this before:

for letter in text:
    result = letter
return result

Hopefully the result variable would store all of the iterations instead of just replacing the old with the new… or would I need to append each iteration to the variable?

I need to ensure the iteration does not error if it encounters punctuation or a number. I thought by including two things, 1. ‘if in dictionary’ and 2. ‘else continue’ it would skip anything not in the dictionary and continue iterating through the loop.

I know there are probably easier ways to do what I’m trying to do, but I’m still doing push ups and learning the basic moves… the actual kung fu comes later :grinning:

def alphabet_position(text):
    alphabet = {
        'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7,
        'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14,
        'o': 15, 'p': 16, 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21,
        'v': 22, 'w': 23, 'x': 24, 'y': 25, 'z': 26
    }
    Converted = []
    for letter in text.lower():
        if letter in alphabet:
            Converted.append(alphabet[letter])
    print(*Converted)

is my new code and it’s giving me the results I want but not passing the tests…not sure why…it looks the same to me:

Test Results:

Log

20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11

None should equal ‘20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11’

Log

20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20

None should equal ‘20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20’

None should equal ‘’

Only thing I can think is it needs to be a string… but when I try to convert the variable to a string it tells me “str can modify at most 3 and there are 27”

Hi Brad,

The alphabet_position function only prints the value and does not return it.
So you should add,

    return Converted

to compare with the expected value outside the function.

Yet another solution.

>>> message = "The sunset sets at twelve o' clock."
>>> [ord(x) - ord('a') + 1 for x in message.lower() if x.isalpha()]
[20, 8, 5, 19, 21, 14, 19, 5, 20, 19, 5, 20, 19, 1, 20, 20, 23, 5, 12, 22, 5, 15, 3, 12, 15, 3, 11]
1 Like

I tried using return and it yields:

Test Results:

[20, 8, 5, 19, 21, 14, 19, 5, 20, 19, 5, 20, 19, 1, 20, 20, 23, 5, 12, 22, 5, 15, 3, 12, 15, 3, 11] should equal ‘20 8 5 19 21 14 19 5 20 19 5 20 19 1 20 20 23 5 12 22 5 15 3 12 15 3 11’

[20, 8, 5, 14, 1, 18, 23, 8, 1, 12, 2, 1, 3, 15, 14, 19, 1, 20, 13, 9, 4, 14, 9, 7, 8, 20] should equal ‘20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20’

[ ] should equal ’ ’

or

Traceback (most recent call last):
File “/workspace/default/tests.py”, line 1, in
from solution import *
File “/workspace/default/solution.py”, line 12
return (*Converted)
^^^^^^^^^^
SyntaxError: cannot use starred expression here

apparently, the solution must be one string with no commas. The only way I can think to do this is to append a single string with each integer in a separate for loop…

def alphabet_position(text):
    alphabet = {
        'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5, 'f': 6, 'g': 7,
        'h': 8, 'i': 9, 'j': 10, 'k': 11, 'l': 12, 'm': 13, 'n': 14,
        'o': 15, 'p': 16, 'q': 17, 'r': 18, 's': 19, 't': 20, 'u': 21,
        'v': 22, 'w': 23, 'x': 24, 'y': 25, 'z': 26
    }
    Converted = []
    for letter in text.lower():
        if letter in alphabet:
            Converted.append(alphabet[letter])
    st_conv = [str(x) for x in Converted]
    final = " ".join(st_conv)
    return final

Hallelujah!! it worked. I’m sure this could all be combined into one list comprehension line but at least I understand the steps it has to go through! Yay!