Using a dict to translate numbers to letters in python - python

I have a string 'alphabet' with all the letters in the alphabet and a list of ints corresponding to those those letters (0-25).
Ex:
num_list = [5,3,1] would translate into letter_list = ['f','d','b']
I currently can translate with:
letter_list = [alphabet[a] for a in num_list]
However, I would like to use a dict for the same thing, retrieve 'letter' keys from dict with 'number' values.
alpha_dict = {'a':0,'b':1,'c':2}... etc
How do I change my statement to do this?

Simply iterate through your alphabet string, and use a dictionary comprehension to create your dictionary
# Use a dictionary comprehension to create your dictionary
alpha_dict = {letter:idx for idx, letter in enumerate(alphabet)}
You can then retrieve the corresponding number to any letter using alpha_dict[letter], changing letter to be to whichever letter you want.
Then, if you want a list of letters corresponding to your num_list, you can do this:
[letter for letter, num in alpha_dict.items() if num in num_list]
which essentially says: for each key-value pair in my dictionary, put the key (i.e. the letter) in a list if the value (i.e. the number) is in num_list
This would return ['b', 'd', 'f'] for the num_list you provided

You could also invert alpha_dict to do this:
>>> import string
>>> alpha_dict = dict(enumerate(string.ascii_lowercase))
>>> alpha_dict
{0: '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'}
>>> numbers = [5, 3, 1]
>>> [alpha_dict[x] for x in numbers]
['f', 'd', 'b']
This way your dictionary maps numbers to letters, instead of letters to numbers.

You can build a dict of letter/numbers as such:
import string
alpha_dict = {
i: string.ascii_lowercase[i]
for i in list(range(0, len(string.ascii_lowercase)))
}
num_list = [5,3,1]
letter_list = [alpha_dict.get(a) for a in num_list]
However it would be easier just to use the list like this:
import string
num_list = [5,3,1]
letter_list = [string.ascii_lowercase[a] for a in num_list]

I think -- like the other answers -- that a list should be used (to utilize the handy index calling) but dictionaries should NOT be used, period. SO...
from string import ascii_lowercase # ONLY import ascii_lowercase
numList = [numbers, in, your, list] # like you said in your question
translation = "".join ([ascii_lowercase[num] for num in numList])

letters = [chr(i) for i in range(97, 123)]
alph_index = dict(zip(letters, range(26)))
print(">> letters=", letters)
print(">> alph_index=", alph_index)
ord('a') = 97
chr(97) = 'a'
97 is a, 65 is A, 122 is z
Output:
>> letters= ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
>> alph_index= {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4, 'f': 5, 'g': 6, 'h': 7, 'i': 8, 'j': 9, 'k': 10, 'l': 11, 'm': 12, 'n': 13, 'o': 14, 'p': 15, 'q': 16, 'r': 17, 's': 18, 't': 19, 'u': 20, 'v': 21, 'w': 22, 'x': 23, 'y': 24, 'z': 25}

Related

Why this dictionary values are all the same?

I am trying to write this dictionary comprehension and I don't know why all the dictionary values are the same.
The code is:
from string import ascii_uppercase
dictionary = {key: value for key in range(0, len(ascii_uppercase)) for value in ascii_uppercase}
The result is:
{0: 'Z', 1: 'Z', 2: 'Z', 3: 'Z', 4: 'Z', 5: 'Z', 6: 'Z', 7: 'Z', 8: 'Z', 9: 'Z', 10: 'Z', 11: 'Z', 12: 'Z', 13: 'Z', 14: 'Z', 15: 'Z', 16: 'Z', 17: 'Z', 18: 'Z', 19: 'Z', 20: 'Z', 21: 'Z', 22: 'Z', 23: 'Z', 24: 'Z', 25: 'Z'}
Why it is only giving me the last character of the string as all the values?
How can I fix it?
If you convert the dict comprehension into regular loops, you have this:
dictionary = {}
for key in range(0, len(ascii_uppercase)): # for every number in range 0-26
for value in ascii_uppercase: # for every letter in range a-z
dictionary[key] = value # d[num] = letter
The last letter for every number is 'z' so your dictionary is updated with it at the end of inner loop for each number.
You can try:
di = {}
for i, letter in enumerate(ascii_uppercase):
di[i] = letter
or
di = {i: letter for i, letter in enumerate(ascii_uppercase)}
or
di = dict(enumerate(ascii_uppercase))
Because the inner loop always end as the end of list, better is to Use enumerate() with dict() for this:
dictionary = dict(enumerate(ascii_uppercase))
The second for-loop gets fully executed before the first one so it messes up the result. You can try this approach with just one for-loop instead:
dictionary = {i: ascii_uppercase[i] for i in range(len(ascii_uppercase))}
Change your code to this
from string import ascii_uppercase
dictionary = {key: value for key,value in enumerate(ascii_uppercase, 0)}
Output
{0: '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'}
While there are other suitable answers, I believe this is what you think you are doing.
from string import ascii_uppercase
{key: value for key,value in zip(range(0, len(ascii_uppercase)),ascii_uppercase)}

Is there are more efficient method for creating a dictionary of letters mapped to letters shifted by a set integer?

I have written a function which takes an integer and returns a dictionary where the letters of the alphabet are mapped to an alphabet shifted by the integer. It has the requirement that lowercase and uppercase letters must correpsond, that is if a -> b then A->B.
I'm fairly new to python and have little experience with creating lists so I'm just wondering if there is an alternative more efficient or even just more elegant way to complete the same outcome.
import string
dictionary = []
for letter in string.ascii_lowercase + string.ascii_uppercase:
dictionary.append(letter)
def build_shift(shift):
shifted_dictionary = []
for i in range(0,26):
shifted_dictionary.append(dictionary[(i + shift) % 26])
for i in range(26, 53):
shifted_dictionary.append(dictionary[(( i + shift) % 26) + 26])
return (shifted_dictionary)
mapped_dictionary = dict( zip(dictionary, build_shift(2)))
print(mapped_dictionary)
You can use a dict comprehension that iterates characters over each of the lowercase and uppercase strings to output the character mapped to the one at the current index plus the offset and reduced with a modulo of 26. Below is an example using 2 as the offset:
{
c: s[(i + 2) % 26]
for s in (string.ascii_lowercase, string.ascii_uppercase)
for i, c in enumerate(s)
}
You can slice the string and get the desired result in less number of operations.
import string
shift = 2
changed_lc = f'{string.ascii_lowercase[shift:]}{string.ascii_lowercase[:shift]}'
changed_up = f'{string.ascii_uppercase[shift:]}{string.ascii_uppercase[:shift]}'
changed = f'{changed_lc}{changed_up}'
mapped_dictionary = dict(zip(string.ascii_letters, changed))
print(mapped_dictionary)
output:
{'a': 'c', 'b': 'd', 'c': 'e', 'd': 'f', 'e': 'g', 'f': 'h', 'g': 'i', 'h': 'j', 'i': 'k', 'j': 'l', 'k': 'm', 'l': 'n', 'm': 'o', 'n': 'p', 'o': 'q', 'p': 'r', 'q': 's', 'r': 't', 's': 'u', 't': 'v', 'u': 'w', 'v': 'x', 'w': 'y', 'x': 'z', 'y': 'a', 'z': 'b', 'A': 'C', 'B': 'D', 'C': 'E', 'D': 'F', 'E': 'G', 'F': 'H', 'G': 'I', 'H': 'J', 'I': 'K', 'J': 'L', 'K': 'M', 'L': 'N', 'M': 'O', 'N': 'P', 'O': 'Q', 'P': 'R', 'Q': 'S', 'R': 'T', 'S': 'U', 'T': 'V', 'U': 'W', 'V': 'X', 'W': 'Y', 'X': 'Z', 'Y': 'A', 'Z': 'B'}

Python. How to modify/replace elements of a list which is a value in a dictionary?

I have two dictionaries:
D_1 = {1: 'A', 2: 'B', 3: 'C', 4: 'D', 5: 'E', 6: 'F', 7: 'G', 8: 'H', 9: 'I', 10: 'J', 11: 'K', 12: 'L', 13: 'M', 14: 'N', 15: 'O', 16: 'P', 17: 'Q', 18: 'R', 19: 'S', 20: 'T', 21: 'U', 22: 'V', 23: 'W', 24: 'X', 25: 'Y', 26: 'Z'}
D_2 = {1: ['A', 'A', 'R', 'O', 'N'], 2: ['A', 'B', 'B', 'E', 'Y'], 3: ['A', 'B', 'B', 'I', 'E']}
My task is to replace the letters in values of D2 with corresponding keys from D1. For example I would want the first key and value pair in D2 to look like this : {1:[1,1,19,16,15]}
I have modified D2 where i made the values into lists instead of strings thinking that it would make my task easier.
dd = {v:k for (k, v) in D_1.items()}
{x: [dd[a] for a in y] for (x, y) in D_2.items()}
First line reverts D_1. Second line applies dd to values in D_2.
I think your D_1 is redundant. What you can do here is to use ascii_uppercase:
from string import ascii_uppercase
{k: [ascii_uppercase.index(i) + 1 for i in v] for k, v in D_2.items()}
Output:
{1: [1, 1, 18, 15, 14], 2: [1, 2, 2, 5, 25], 3: [1, 2, 2, 9, 5]}
Using ord
D_2 = {1: ['A', 'A', 'R', 'O', 'N'], 2: ['A', 'B', 'B', 'E', 'Y'], 3: ['A', 'B', 'B', 'I', 'E']}
{k: [ord(val)-64 for val in v] for (k, v) in D_2.items()}
Output:
{1: [1, 1, 18, 15, 14], 2: [1, 2, 2, 5, 25], 3: [1, 2, 2, 9, 5]}
m = map(lambda v:[{v: k for k, v in D_1.items()}[e] for e in v], D_2.values())
encapsulated in a map and create the new dictionary when useful:
dict(list(enumerate(m, start=1)))
# {1: [1, 1, 18, 15, 14], 2: [1, 2, 2, 5, 25], 3: [1, 2, 2, 9, 5]}

fPython : Making a new list from a random list of letters

letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']#alphabet
bag_o_letters = []#letters to chose from
letter_count = [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1]#random indexes to chose from
for x in range(26):#loops through the random index of letter_count
for i in range(letter_count[x]):#chooses the index
bag_o_letters.append(letters[x])#appends the index of the letter to bag_o_letters
rack = []#list for the person to see
for a in range(7):#makes the list 7 letters long
rack.append(bag_o_letters.pop(random.randint(0,len(letters)-1)))#appends the letter to rack(supposedly...)
print(rack)
In this code that you just read it should choose random letters and put 7 of those letters in a rack that the person can see. It shows a error that I've looked over many times, but I just can't see what is wrong.
I put comments on the side to understand the code.
It shows this error:
rack.append(bag_of_letters.pop(random.randint(0,len(letters)-1)))
IndexError: pop index out of range
Can someone please help?
After this code, I am going to make a input statement for the user to make a word from those letters.
The first time through the loop, you append one value to bag_of_letters, and then you try to pop an index of random.randint(0,len(letters)-1). It doesn't have that many elements to pop from yet. Instead of this approach, you can make a list of the required length and sample from it:
letters = ['a', ...]#alphabet
letter_count = [9, ...]#random indexes to chose from
bag_of_letters = [l*c for l,c in zip(letters, letter_count)]
...
rack = random.sample(bag_o_letters, 7)
You're selecting the index to pop for bag_of_letters from the length of letters which is obviously larger.
You should instead do:
rack.append(bag_of_letters.pop(random.randint(0, len(bag_of_letters)-1)))
# ^^^^^^^^^^^^^^
However, there are likely to be more problems with your code. I'll suggest you use random.sample in one line of code or random.shuffle on a copy of the list, and then slice up till index 7. Both will give you 7 randomly selected letters:
import random
print(random.sample(letters, 7))
# ['m', 'u', 'l', 'z', 'r', 'd', 'x']
import random
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
letters_copy = letters[:]
random.shuffle(letters_copy)
print(letters_copy[:7])
# ['c', 'e', 'x', 'b', 'w', 'f', 'v']
The IndexError is expected:
pop(...)
L.pop([index]) -> item -- remove and return item at index (default last).
Raises IndexError if list is empty or index is out of range.
You need to subtract 1 from the bounds of the call to random() after each pop(). Right now you are doing this:
l = [1,2,3]
random_idx = 2
l.pop(random_idx)
>>> l == [1,3]
random_idx = 3
l.pop(random_idx)
>>>> IndexError: pop index out of range
So instead, pop() based on len(bag_o_letter) rather than len(letter).
Why not do something like this:
letters = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
letter_count = [9, 2, 2, 4, 12, 2, 3, 2, 9, 1, 1, 4, 2, 6, 8, 2, 1, 6, 4, 6, 4, 2, 2, 1, 2, 1]#random indexes to chose from
from random import shuffle
all_letters = list(''.join([l*c for l,c in zip(letters, letter_count)]))
shuffle(all_letters)
for i in range(int(len(all_letters)/7)):
print all_letters[i*7:(i+1)*7]
So I assume this is for something like scrabble? Your issue is that you're choosing a random index from your list of letters, not bag_o_letters. Maybe try this:
rack = []
for i in range(7):
index = random.randint(0, len(bag_o_letter) - 1)
rack.append(bag_o_letters.pop(index))

Why my code prints others characters? cipher

I want print only letters but it's prints special characters of ASCII. My code:
import string
def caesar(shift):
alphabet = string.ascii_lowercase + string.ascii_uppercase
dict={}
emptylist=[]
int(shift)
for x in alphabet:
emptylist.append(x)
code = ""
for letters in emptylist:
code = chr(ord(letters) + shift)
dict[letters]=code
return dict
caesar(12)
My output:
'm': 'y', 'l': 'x', 'o': '{', 'n': 'z', 'q': '}', 'p': '|', 's': '\x7f', 'r': '~', 'u': '\x81', 't': '\x80', 'w': '\x83', 'v': '\x82', 'y': '\x85', 'x': '\x84', 'z': '\x86'
Correct output:
'm': 'y', 'l': 'x', 'o': 'a', 'n': 'z', 'q': 'c', 'p': 'b', 's': 'e', 'r': 'd', 'u': 'g', 't': 'f', 'w': 'i', 'v': 'h', 'y': 'k', 'x': 'j', 'z': 'l'
Using ord() and changing the character code won't restrict the resulting character to your dictionary.
I'd just find the index of the letter in your dictionary, shift it, and use the modulo operator:
import string
def caesar(shift):
alphabet = string.ascii_uppercase # <- Change it back to what you had before
# and see what will happen.
mapping = {}
for letter in alphabet:
index = alphabet.index(letter)
mapping[letter] = alphabet[(index + shift) % len(alphabet)]
return mapping
Test (dictionaries don't preserve order, so it's pretty hard to read):
>>> caesar(12)
{'A': 'M', 'C': 'O', 'B': 'N', 'E': 'Q', 'D': 'P', 'G': 'S', 'F': 'R', 'I': 'U', 'H': 'T', 'K': 'W', 'J': 'V', 'M': 'Y', 'L': 'X', 'O': 'A', 'N': 'Z', 'Q': 'C', 'P': 'B', 'S': 'E', 'R': 'D', 'U': 'G', 'T': 'F', 'W': 'I', 'V': 'H', 'Y': 'K', 'X': 'J', 'Z': 'L'}
Let's look at one error in particular: o: '{'.
Notice that ord('o') is 111, so let's look at the chr of integers in the range(111,130):
Starting at o, shifting by 12, takes you to the { character:
In [75]: ' '.join([chr(x) for x in range(111,130)])
Out[75]: 'o p q r s t u v w x y z { | } ~ \x7f \x80 \x81'
^ 1 2 3 4 5 6 7 8 9 ...12
So the reason why you are getting incorrect output is because your formula
code = chr(ord(letters) + shift)
isn't taking into account what happens if the shift bounces you out of the ords associated with a-z or A-Z. (Note that the ord ranges for a-z and A-Z are not contiguous either!)
Here is a hint on how to fix:
In [82]: alphabet = string.ascii_lowercase + string.ascii_uppercase
In [83]: alphabet.index('o')
Out[83]: 14
In [84]: alphabet[alphabet.index('o')+12]
Out[84]: 'A'
but
In [85]: alphabet[alphabet.index('O')+12]
results in IndexError: string index out of range. That's because len(alphabet) is 52, and
In [91]: alphabet.index('O')+12
Out[91]: 52
Somehow we need 52 to wrap back around to 0. You can do that with the % modulo operator:
In [92]: 52 % 52
Out[92]: 0

Categories

Resources