This question already has answers here:
Why is the order in dictionaries and sets arbitrary?
(5 answers)
Closed 6 years ago.
I've been searching everywhere for a problem that occurred in one of my codes, but didn't see any solution... It's been pretty annoying and that is why I'm asking you this question.
I'm using a simple dictionnary with keys and values, but the problem is when I want to print it at the end it has a strange shuffle where two letters are inverted, like for example "C":"W", "B":"X" instead of "B":"X", "C":"W". Here is my code (it will probably be clearer)
PS. I've first tried in the last few lines to replace the while structure by a for structure, with no improvement.
import random
m1 = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
l1 = ["ABCDEFGHIJKLMNOPQRSTUVWXYZ"]
class Separate:
#Used to separate objects in a list
def __init__(self, d):
self.d = d
self.d = "".join(self.d)
l = []
for elt in range(0, len(self.d)):
l.append(self.d[elt])
self.d = l
l1 = Separate(l1)
l1 = l1.d
m1 = Separate(m1)
m1 = m1.d
random.shuffle(m1)
key = {}
count = 0
while count < len(l1):
key[l1[count]] = m1[count]
count+=1
print(key)
This returns (for example) :
{
'A': 'G',
'C': 'A',
'B': 'Z',
'E': 'U',
'D': 'I',
'G': 'W',
'F': 'X',
'I': 'C',
'H': 'K',
'K': 'T',
'J': 'E',
'M': 'F',
'L': 'B',
'O': 'V',
'N': 'D',
'Q': 'M',
'P': 'L',
'S': 'S',
'R': 'J',
'U': 'Q',
'T': 'Y',
'W': 'H',
'V': 'R',
'Y': 'P',
'X': 'N',
'Z': 'O'
}
As mentioned in the comments, dicts don't have an order. If you need for your dict to track order, consider using an collections.OrderedDict
viz:
import collections
key = collections.OrderedDict()
You can do it using two string literals and zip:
m1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
l1 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
key = {}
for m,l in zip(m1,l1):
key.update({m:l})
print(key)
~
Related
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.
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
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)
I am a beginner to list comprehension and am having trouble figuring something out. According to examples I have looked at on stackoverflow and other sites, I have a list comprehension that seems like it should work, but I have not been able to accomplish the desired output, as I have been unable to figure out the correct syntax for what I want to accomplish.
Given a string, I would like my function to return the string with the alpha characters replaced with the value associated with a key in the provided dictionary. For that task my list comprehension works, but I also need any characters and spaces to stay intact (no change).
Here is what I have tried:
#random dictionary for my example
d = {'a': 'b', 'c': 'i', 'b': 'a', 'e': 'j', 'd': 'm', 'g': 'q','f': 'l',
'i': 'c', 'h': 'w', 'k': 'r', 'j': 'e', 'm': 'd','l': 'f', 'o': 'v',
'n': 's', 'q': 'g', 'p': 't', 's': 'n','r': 'k', 'u': 'x', 't': 'p',
'w': 'h', 'v': 'o', 'y': 'z', 'x': 'u', 'z': 'y'}
def cipher(message):
word = list(message)
word = [v for x in word for k,v in d.iteritems() if x == k]
#word = [v for x in word for k,v in d.iteritems() if x == k else x for x in word]
return "".join(word)
print cipher("that tree is far away!")
This returns my string with the alpha characters correctly changed, but with no spaces and with no ! mark. From further research, that lead me to try the else statement I have in the list comprehension that is commented out in my code example, but that doesn't work.
Can I edit my syntax or can I not accomplish what I am trying to do using list comprehension?
To further clarify:
I am receiving this output: pwbppkjjcnlbkbhbz
I want this output: pwbp pkjj cn lbk bhbz!
Your current approach filters out all characters that are not in the dictionary viz. whitespace and the exclamation.
You could use the .get method of the dictionary instead to fetch replacements and return the original character when a replacement character does not exist in your mapping:
def decipher(message):
return "".join(d.get(x, x) for x in message)
print decipher("that tree is far away!")
#pwbp pkjj cn lbk bhbz!
Note that strings are iterable so word = list(message) is really not necessary and can be dropped.
On a another note, the name of the function probably reads better as cipher
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!