Hi! Today we will take a look on the competition between reg exps or.. their non-existence.
Atbash Cipher
Let's take a look on a quite interesting, but still simple task - to write Atbash Cipher
Create an implementation of the atbash cipher, an ancient encryption system created in the Middle East.
The Atbash cipher is a simple substitution cipher that relies on transposing all the letters in the alphabet such that the resulting alphabet is backwards. The first letter is replaced with the last letter, the second with the second-last, and so on.
An Atbash cipher for the Latin alphabet would be as follows:
Plain: abcdefghijklmnopqrstuvwxyz
Cipher: zyxwvutsrqponmlkjihgfedcba
First let's take a look on excellent solution from Bob.
Bob's solution
export function encode(plainText: string, isDecoding:boolean = false): string {
let output: number[] = [];
let count = 0;
const aAscii = 'a'.charCodeAt(0);
const zAscii = 'z'.charCodeAt(0);
let cAscii: number;
for (let c of [...plainText.toLowerCase()]){
if (c >= 'a' && c <= 'z')
cAscii = zAscii - (c.charCodeAt(0) - aAscii);
else if (c >= '0' && c <= '9')
cAscii = c.charCodeAt(0);
else
continue;
if (count == 5) {
if (!isDecoding) output.push(32);
count = 0;
}
output.push(cAscii);
count ++;
}
return String.fromCharCode.apply(null, output);
}
export function decode(cipherText: string): string {
return encode(cipherText, true);
}
Pros
The code is well structured.
Cons
- The method
encode
includes an additional flag as a parameter which looks redundant and make logic more complex. - All logic at one place. The method can be easily splitted into few smaller.
- Some values can be presented as consts to remove "magic" chars and numbers from the code.
- we can make function more
functional
by replacinglet
s withconst
s and providingchaining
for greater readability. - Let's introduce Reg Exps to remove redundant logic!
Possible refactoring
As we can see - the list of possible changes quite big. But the question is - will it help to the end user? Let's take a look!
After some changes
After all mentioned above things applied - the new code is looking like that:
const PUNCTUATION_AND_SPACE_REGEX = /[\s,.!?]/g;
const GROUP_OF_FIVE_REGEX = /(.{1,5})/g;
const A_CODE = 'a'.charCodeAt(0);
const Z_CODE = 'z'.charCodeAt(0);
const groupText = (text: string) => (text.match(GROUP_OF_FIVE_REGEX) ?? []).join(" ");
const isAlphabeticChar = (code: number) => code >= A_CODE && code <= Z_CODE;
const encodeChar = (char: string) => {
const charCode = char.charCodeAt(0);
const newCharCode = isAlphabeticChar(charCode) ? A_CODE + (Z_CODE - charCode) : charCode;
return String.fromCharCode(newCharCode);
}
const cleanText = (text: string) => text
.toLowerCase()
.replaceAll(PUNCTUATION_AND_SPACE_REGEX, "");
const applyCipher = (text: string) => [...cleanText(text)]
.map(encodeChar)
.join("")
const encode = (plainText: string): string => groupText(applyCipher(plainText));
const decode = (cipherText: string): string => applyCipher(cipherText);
Please, put in comments what solution you prefer more. Also do not hesitate to left links in Exercism for your own solutions - sharing is caring ;)
Possible improvement: encapsulate logic inside class. Waitting for your links to solutions in the comments!
Meanwhile you can take a look on the task where I did some useful things regarding TS types.
Top comments (0)