I'm trying to replace the characters of the reversed alphabet with those of the alphabet. This is what I've got:
alphabet = ['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']
rev_alphabet = alphabet[::-1]
sample = "wrw blf hvv ozhg mrtsg'h vkrhlwv?"
def f(alph, rev_alph):
return (alph, rev_alph)
char_list_of_tups = list(map(f, alphabet, rev_alphabet))
for alph, rev_alph in char_list_of_tups:
sample = sample.replace(rev_alph, alph)
print(sample)
expected output: did you see last night's episode?
actual output: wrw you svv ozst nrtst's vprsowv?
I understand that I'm printing the last "replacement" of the whole iteration. How can I avoid this without appending it to a list and then running into problems with the spacing of the words?
3 Answers 3
Your problem here is that you lose data as you perform each replacement; for a simple example, consider an input of "az"
. On the first replacement pass, you replace 'z'
with 'a'
, and now have "aa"
. When you get to replacing 'a'
with 'z'
, it becomes "zz"
, because you can't tell the difference between an already replaced character and one that's still unchanged.
For single character replacements, you want to use the str.translate
method (and the not strictly required, but useful helper function, str.maketrans
), to do character by character transliteration across the string in a single pass.
from string import ascii_lowercase # No need to define the alphabet; Python provides it
# You can use the original str form, no list needed
# Do this once up front, and reuse it for as many translate calls as you like
trans_map = str.maketrans(ascii_lowercase[::-1], ascii_lowercase)
sample = sample.translate(trans_map)
alphabet = ['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']
# or
alphabet = [chr(97 + i) for i in range(0,26)]
sample = "wrw blf hvv ozhg mrtsg'h vkrhlwv?"
res = []
for ch in sample:
if ch in alphabet:
res.append(alphabet[-1 - alphabet.index(ch)])
else:
res.append(ch)
print("".join(res))
Another Way if you are ok with creating a new string instead.
alphabet = ['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']
dictRev = dict(zip(alphabet, alphabet[::-1]))
sample = "wrw blf hvv ozhg mrtsg'h vkrhlwv?"
s1="".join([dictRev.get(char, char) for char in sample])
print(s1)
"did you see last night's episode?"
-
1They must be okay with creating a new string; strings aren't mutable so any "mutation" makes a new one (their old code made 26 new strings). Your code could still reassign the result of the
"".join
back tosample
and work just fine, the key is that you only performed one replacement per character, you didn't perform 26 such replacements). For all practical purposes, your code is a deoptimized version ofstr.translate
(which is, internally, just a loop over the input doing the optimized C-level equivalent of what your"".join([dictRev.get(char, char) for char in sample])
is doing).ShadowRanger– ShadowRanger2022年06月17日 14:41:35 +00:00Commented Jun 17, 2022 at 14:41
str.maketrans
andstr.translate
to do the translation in one pass and avoid the issue you are describing.list(map(f, alphabet, rev_alphabet))
is a slow (and verbose since you have to definef
) way to spelllist(zip(alphabet, rev_alphabet))
.