function that creates a new words by given pattern - python

im trying to create a function that takes a word (capital letters and lowercase letters) and map each character to a new character. the pattern is each vowel (AEIOU) becomes the next vowel in order (A -> E, E - > I). For constant letters becomes the thirds letter (B -> F, C -> G)
>>>'hello'
'lippu'
>>> 'today'
'xuhec'
>>> 'yesterday'
'ciwxivhec'
I know that i would have to create two lists:
vowels = ['a', 'e', 'i', 'o', 'u']
constants = ['b', 'c','d','f','g','h','j','k','l','m','n','p', 'q','r', 's','t','v','w','x','y', 'z']
and uses the index() function, to check the current index and add 3 to it, but im stuck after that.
Letters loop back around for cases that extend beyond the list. (x-z, and u)

To compute the maps you can use enumerate (to get the index of the current) together with modulo (for the indices greater than the list length), something like this:
vowels = ['a', 'e', 'i', 'o', 'u']
consonants = ['b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'y', 'z']
vowels_map = {k: vowels[(i + 1) % len(vowels)] for i, k in enumerate(vowels)}
consonants_map = {k: consonants[(i + 3) % len(consonants)] for i, k in enumerate(consonants)}
print(vowels_map)
print(consonants_map)
Output
{'u': 'a', 'a': 'e', 'o': 'u', 'e': 'i', 'i': 'o'}
{'s': 'w', 'z': 'd', 'v': 'y', 'm': 'q', 'f': 'j', 'h': 'l', 'd': 'h', 'g': 'k', 'q': 't', 'n': 'r', 'p': 's', 'k': 'n', 't': 'x', 'y': 'c', 'r': 'v', 'w': 'z', 'x': 'b', 'l': 'p', 'b': 'f', 'j': 'm', 'c': 'g'}
Note that dictionaries have no order, that being said you could use them in the following way:
def replace_from_dict(word, table):
return ''.join(table[c] for c in word)
words = ['hello',
'today',
'yesterday']
for word in words:
print(replace_from_dict(word, { **vowels_map, **consonants_map }))
Output (from using replace_from_dict)
lippu
xuhec
ciwxivhec

We can use itertools.cycle. First check which category i belongs to either vowel or consonants (not constant). Then create a cycle from that respective list, use while and next until we are at the corresponding letter. If its a vowel we simply append the next value, if its a consonant we advance 2 positions and then append the next value. After use .join() to convert back to string.
from itertools import cycle
vwl = ['a', 'e', 'i', 'o', 'u']
cnst = ['b', 'c','d','f','g','h','j','k','l','m','n','p', 'q','r', 's','t','v','w','x','y', 'z']
s = 'hello'
new = []
for i in s.lower():
if i in vwl:
a = cycle(vwl)
while i != next(a):
next(a)
new.append(next(a))
if i in cnst:
b = cycle(cnst)
while i != next(b):
next(b)
for x in range(2):
next(b)
new.append(next(b))
res = ''.join(new)
print(res)
# lippu
Works for words that include edge letters, zumba produces daqfe

I have defined two dictionaries for edge cases, vowel_dictionary, and a dictionary for letters x/y/z to achieve wrapping. I iterated through the string and if the character is a special character, I use the appropriate dictionary to find the word. However if the character is below 'w' and not a vowel, I just add 4 to its ord value (ASCII value) and convert it char.
def transform(input):
return_string = ""
vowel_dictionary = {
'a': 'e',
'e': 'i',
'i': 'o',
'o': 'u',
'u': 'a'
}
edge_dictionary = {
'x': 'b',
'y': 'c',
'z': 'd'
}
for character in input.lower():
if character in vowel_dictionary:
return_string += vowel_dictionary[character]
elif ord(character) <= ord("v"):
return_string += chr(ord(character) + 4)
else :
return_string += edge_dictionary[character]
return return_string
I have ran a few tests with the code above:
Tests
transform("hello") # => lippu
transform("today") # => xuhec
transform("yesterday") # => ciwxivhec

Related

decrypting song with dictionary values

Hi I want to decrypt the song down below, but when I replace some word
and I keep moving forward in the dictionary keys, I lose my replacement.
For example, when 'O' turns to 'A' it is ok but, when the code continues
the dictionary value in 'A' = 'U'.
So I lose the correct replacement, and I get a wrong answer.
decryption_key = {
'O': 'A', 'D': 'B', 'F': 'C', 'I': 'D', 'H': 'E',
'G': 'F', 'L': 'G', 'C': 'H', 'K': 'I', 'Q': 'J',
'B': 'K', 'J': 'L', 'Z': 'M', 'V': 'N', 'S': 'O',
'R': 'P', 'M': 'Q', 'X': 'R', 'E': 'S', 'P': 'T',
'A': 'U', 'Y': 'V', 'W': 'W', 'T': 'X', 'U': 'Y',
'N': 'Z',
}
SONG = """
sc, kg pchxh'e svh pckvl k covl svps
pcop lhpe zh pcxsalc pch vklcp
k okv'p lsvvo is wcop k isv'p wovp ps
k'z lsvvo jkyh zu jkgh
eckvkvl jkbh o ikozsvi, xsjjkvl wkpc pch ikfh
epovikvl sv pch jhilh, k ecsw pch wkvi csw ps gju
wchv pch wsxji lhpe kv zu gofh
k eou, coyh o vkfh iou
coyh o vkfh iou
"""
SONG = SONG.upper()
for word, value in decryption_key.items():
if word in SONG:
SONG = SONG.replace(word, value)
else:
continue
print(SONG)
One workaround could be:
SONG = SONG.replace(word, value.lower())
This way, letters that are replaced will be lowercased and won't be affected later.
But a proper way would be constructing a new string:
SONG = ''.join(decryption_key.get(c, c) for c in SONG)
This just replaces your whole loop.
decryption_key.get(c, c) part might be confusing, but what it does is it returns the value by the given key if it exists in decryption_key or returns the key itself (as it was specified as a default by the second parameter) otherwise.

How to make dictionary of alphabet and scrambled alphabet?

I need to create multiple dictionaries for another program using the alphabet as the key and a scrambled alphabet (the first one I have to do is BDFHJLCPRTXVZNYEIWGAKMUSQO) as the value. This is what I have so far but it says "unhashable type: 'list'"
_list = input("Enter list: ")
alpha = ['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']
dict1 = {}
for i in range(0,len(_list)):
dict1[alpha][i] == _list[i]
print(dict1)
There are two errors in your code.
dict1[alpha][i] should be dict1[alpha[i]]
As pointed out by Paul Rooney, you cannot use a list as a dictionary key, but I don't think that is what you are trying to do. Instead, you just want the ith element of the list alpha.
the == should be =.
== is used to compare, = is used to assign a value.
Also, the pythonic way to do your iteration is as follows:
dict1 = {alpha[idx]: character for idx, character in enumerate(_list)}
which is the equivalent of
dict1 = {}
for idx, character in enumerate(_list):
dict1[alpha[idx]] = character

Decoding message using dictionary on string in Python

When I'm using a dictionary to replace values in a string for decoding of a message, how do I put it in such that the function doesn't read a replaced value as a key and replace a replaced value again?
def decipher_message(translation_guide, message):
t = read_translation_guide_into_dictionary(translation_guide)
e = read_message(message)
print(t)
print(e)
for key, value in t.items():
f = e
e = f.replace(key, value)
return e
Output:
{'a': 'm', 'b': 'a', 'c': 'c', 'd': 'y', 'e': 't', 'f': 'v', 'g': 'o',
'h': 'u', 'i': 'x', 'j': 'e', 'k': 'j', 'l': 'w', 'm': 'f', 'n': 'z',
'o': 'd', 'p': 'l', 'q': 'i', 'r': 'k', 's': 'h', 't': 'n', 'u': 'g',
'v': 'b', 'w': 'q', 'x': 's', 'y': 'p', 'z': 'r'}
"qa mqtbppd vjqtu mghto! esbtr dgh mgz mqtoqtu aj gs rqto xezbtujz. q
ahxe ejpp dgh, qex whqej vgzqtu sbfqtu bpp esj ljbpes qt esj lgzpo. qm
dghzj pggrqtu mgz qe, q vhzqjo b aby eg qe bzghto lsjzj dghzj xebtoqtu
tgl! zjajavjz esghus: ljbpes qxte jfjzdesqtu qt esj lgzpo!"
'"if finallp being fdgnd! nhank pdg fdr finding fe dh kind snranger. i
fgsn nell pdg, ins qgine bdring habing all nhe qealnh in nhe qdrld. if
pdgre lddking fdr in, i bgried a fap nd in ardgnd qhere pdgre snanding
ndq! refefber nhdggh: qealnh isnn eberpnhing in nhe qdrld!"'
Rather than iterating through the dictionary and running a replace against the whole string, you should iterate through the string and replace each character with its value in the dict:
decoded = []
for letter in e:
decoded.append(t.get(letter, letter))
return ''.join(decoded)
Note also that Python has a built-in string translate method, which takes a table which can be generated from your dict:
table = str.maketrans(t)
return e.translate(table)
You can use str.join with the following generator expression that iterates over the string to translate each character:
def decipher(translation, message):
t = read_translation(translation)
e = read_message(message)
return ''.join(t.get(c, c) for c in e)

'str' object is not callable in caesar encryptor

def encrypt_caesar(plaintext):
s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
d = dict(map(s,s[3:]+ s[:3]))
return ''.join(map(lambda l: d.get(l,l), plaintext.lower()))
Tried to copy this code from Stanford lecture slide and run it but it gives 'str' object is not callable error, what am I doing wrong?
I guess the author of the slide didn't actually test the code.
The code you produced is trying to use map() to call s as a function:
map(s,s[3:]+ s[:3])
# ^ This must be a *callable object*, like a function
If you wanted to create a dictionary mapping ASCII letters to a letter 3 spots along in the alphabet, use the zip() function instead:
d = dict(zip(s, s[3:] + s[:3]))
zip() then pairs up each element in s with each element in s[3:] + s[:3], and those (letter, letter + 3) pairs are then passed to dict() to form key-value pairs:
>>> s = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
>>> dict(zip(s, s[3:] + s[:3]))
{'A': 'D', 'I': 'L', 'L': 'O', 'X': 'A', 'N': 'Q', 'D': 'G', 'S': 'V', 'B': 'E', 'V': 'Y', 'C': 'F', 'K': 'N', 'W': 'Z', 'R': 'U', 'O': 'R', 'T': 'W', 'P': 'S', 'F': 'I', 'J': 'M', 'M': 'P', 'E': 'H', 'Q': 'T', 'H': 'K', 'G': 'J', 'Z': 'C', 'U': 'X', 'Y': 'B'}
Next, your last line will completely fail to do any encryption, because your map only handles uppercase letters, but you lowercased your input. Either produce a lowercase map, or lowercase your input.
Lowercasing the map could look like this:
def encrypt_caesar(plaintext):
s = "abcdefghijklmnopqrstuvwxyz"
d = dict(zip(s, s[3:] + s[:3]))
return ''.join(map(lambda l: d.get(l, l), plaintext.lower()))
or you could just use the string.ascii_lowercase constant:
from string import ascii_lowercase
def encrypt_caesar(plaintext):
d = dict(zip(ascii_lowercase, ascii_lowercase[3:] + ascii_lowercase[:3]))
return ''.join(map(lambda l: d.get(l,l), plaintext.lower()))
Using this method is rather slow, however. For blazingly-fast 'encryption', use the str.translate() method; the input map for that is best produced with str.maketrans:
from string import ascii_lowercase as alc
def encrypt_caesar(plaintext, shift=3):
map = str.maketrans(alc, alc[shift:] + alc[:shift])
return plaintext.lower().translate(map)
I added a shift parameter to define how much of an alphabet shift should be applied.
I'll leave handling both lowercase and uppercase letters as an exercise to the reader!

How to keep "spaces" although dict contains it?

i am simply trying to read from a file encrypt and display it.
And I want to display the result word by word, but somehow spaces are removed (my dict also contains ' ':' ') and result text is displayed without spaces.
For example,
aa bb cc is what i read from file,
Current Output is ffggğğ, but i want it as ff gg ğğ
...please help...
monocrypt = {
'a': 'f',
'b': 'g',
'c': 'ğ',
'ç': 'h',
'd': 'ı',
'e': 'i',
'f': 'j',
'g': 'k',
'ğ': 'l',
'h': 'm',
'ı': 'n',
'i': 'o',
'j': 'ö',
'k': 'p',
'l': 'r',
'm': 's',
'n': 'ş',
'o': 't',
'ö': 'u',
'p': 'ü',
'r': 'v',
's': 'y',
'ş': 'z',
't': 'a',
'u': 'b',
'ü': 'c',
'v': 'ç',
'y': 'd',
'z': 'e',
' ': ' ',
}
inv_monocrypt = {}
for key, value in monocrypt.items():
inv_monocrypt[value] = key
f = open("C:\\Hobbit.txt","r")
print("Reading file...")
message = f.read()
crypt = ''.join(i for i in message if i.isalnum())
encrypted_message = []
for letter in crypt:
encrypted_message.append(monocrypt[letter.lower()])
print(''.join(encrypted_message))
You're dropping all the whitespace at this step
crypt = ''.join(i for i in message if i.isalnum())
So leave them all in there. Use dict.get with the default argument to preserve letters that aren't keys
crypt = f.read()
encrypted_message = []
for letter in crypt:
encrypted_message.append(monocrypt.get(letter.lower(), letter.lower()) )
If you really just want to preserve spaces (and not punctuation/other whitespace etc)
message = f.read()
crypt = ''.join(i for i in message if i.lower() in monocrypt)
encrypted_message = []
for letter in crypt:
encrypted_message.append(monocrypt[letter.lower()])
You can simplify a little like this
message = f.read().lower()
crypt = ''.join(i for i in message if i in monocrypt)
encrypted_message = [monocrypt[letter] for letter in crypt]
Not sure if it works as intended since I don't have hobbit.txt but I made a small rewrite to make the code a little simpler. It should also solve your problem.
monocrypt = {
'a': 'f',
'b': 'g',
'c': 'ğ',
'ç': 'h',
'd': 'ı',
'e': 'i',
'f': 'j',
'g': 'k',
'ğ': 'l',
'h': 'm',
'ı': 'n',
'i': 'o',
'j': 'ö',
'k': 'p',
'l': 'r',
'm': 's',
'n': 'ş',
'o': 't',
'ö': 'u',
'p': 'ü',
'r': 'v',
's': 'y',
'ş': 'z',
't': 'a',
'u': 'b',
'ü': 'c',
'v': 'ç',
'y': 'd',
'z': 'e',
' ': ' ',
}
with open("hobbit.txt") as hobbitfile:
file_text = hobbitfile.read()
crypted_message = ""
for char in file_text:
char = char.lower()
if char in monocrypt:
crypted_message += monocrypt[char]
else:
#I don't know if you want to add the other chars as well.
#Uncomment the next line if you do.
#crypted_message += str(char)
pass
print(crypted_message)
Check the docs :
http://docs.python.org/2/library/stdtypes.html#str.isalnum
isalnum drops your whitespaces.
use this if you want to preserve the space characters, everything else stays the same :
crypt = ''.join(i for i in message if i.isalnum() or i==' ')
If you want to preserve all whitespaces, do this :
crypt = ''.join(i for i in message if i.isalnum() or i.isspace())

Categories