I was trying to program a caesar cipher using the technique of converting letter by letter with an ASCII Converter then shift the numbers and convert them again in text but it works only if the letters don't go over the z with the shift.
const checkPunctuation = (text) => {
return /[.,#!$%^&*;:{}=\-_`~()]/g.test(text);
};
const ASCIIConverter = (text) => {
return text.split("").map((char) => {
return checkPunctuation(char) ? char : char.charCodeAt();
});
};
const caesarCipher = (text, shift) => {
return ASCIIConverter(text)
.map((code) => (code + shift) % 26)
.map((charCode) => String.fromCharCode(charCode))
.join("");
};
console.log(caesarCipher("pizza!", 2));
Expected: wpggh!
Received: (Box Characters with a mark)
Some issues:
There are thousands of characters that are not letters and are not in your punctuation regex. It is better to do a check for letters: if it is not a character from the English alphabet, then leave it unaltered.
return checkPunctuation(char) ? char : char.charCodeAt()
is a bad idea: this means that the mapping is to a mixed type: either string (the original character) or a number (it's code point). This affects the further processing of these values. See next point
.map((code) => (code + shift) % 26)
expects code
to be a number, but it can also be a character (punctuation, see previous point), and then + shift
will perform string concatenation and % 26
will produce NaN
in that case. This obviously is not what is intended.
.map((code) => (code + shift) % 26)
results in numbers between 0 and 25 (and NaN
), which are not the ASCII codes of letters but of control characters.
.map((charCode) => String.fromCharCode(charCode))
will convert control codes (and NaN
) to their character representations, which are those boxes you see. You need to add the ASCII code of the letter 'a' back to it before converting to a string.
The example call is wrong. A shift of 2 would not give "wpggh!" as result. For that you would need to use a shift of 7 (as "w" occurs 7 letters after the letter "p" in the alphabet).
Here is the proposed correction:
const convertRange = (text, regex, a, shift) =>
text.replace(regex, letter =>
String.fromCharCode(
(letter.charCodeAt() - a.charCodeAt() + shift) % 26 + a.charCodeAt()
)
);
const caesarCipher = (text, shift) => {
text = convertRange(text, /[A-Z]/g, 'A', shift); // Cypher uppercase
return convertRange(text, /[a-z]/g, 'a', shift); // Cypher lowercase
};
console.log(caesarCipher("pizza!", 7));