Caesar's Cipher (my attempt) ⚠️ Test Not Passed!
My attempt to clear a Level 6 Kata on Code Wars
Kata Description:
Caesar Ciphers are one of the most basic forms of encryption. It consists of a message and a key, and it shifts the letters of the message for the value of the key.
Read more about it here: https://en.wikipedia.org/wiki/Caesar_cipher
Your task
Your task is to create a function encryptor that takes 2 arguments - key and message - and returns the encrypted message.
Make sure to only shift letters, and be sure to keep the cases of the letters the same. All punctuation, numbers, spaces, and so on should remain the same.
Also be aware of keys greater than 26 and less than -26. There's only 26 letters in the alphabet!
Examples
A message 'Caesar Cipher' and a key of 1 returns 'Dbftbs Djqifs'.
A message 'Caesar Cipher' and a key of -1 returns 'Bzdrzq Bhogdq'.
My Code:
function encryptor(key, message) {
const alphabets = {a: 1, b: 2,c: 3,d: 4,e: 5,f: 6,g: 7,h: 8,i: 9,j: 10,k: 11,l:
12,m: 13,n: 14,o: 15,p: 16,q: 17,r: 18,s: 19,t: 20,u: 21,v: 22,w: 23,x: 24,y: 25,z: 26}
const shiftedAlphabets = {a: 1, b: 2,c: 3,d: 4,e: 5,f: 6,g: 7,h: 8,i: 9,j: 10,k: 11,l:
12,m: 13,n: 14,o: 15,p: 16,q: 17,r: 18,s: 19,t: 20,u: 21,v: 22,w: 23,x: 24,y: 25,z: 26}
const alphabetCap = {a: 1, b: 2,c: 3,d: 4,e: 5,f: 6,g: 7,h: 8,i: 9,j: 10,k: 11,l:
12,m: 13,n: 14,o: 15,p: 16,q: 17,r: 18,s: 19,t: 20,u: 21,v: 22,w: 23,x: 24,y: 25,z: 26}
for (let alpha in alphabetCap) {
alphabetCap[alpha] = 26 - alphabetCap[alpha];
}
if(key < 26 && key > -26) {
for(let letter in shiftedAlphabets) {
shiftedAlphabets[letter] += key;
if(shiftedAlphabets[letter]>26){
let reset = Math.abs(alphabetCap[letter] - key);
shiftedAlphabets[letter] = reset
}
if(shiftedAlphabets[letter]<1) {
shiftedAlphabets[letter] = Math.abs(26 - Math.abs(shiftedAlphabets[letter]))
}
}
}
let newMessage = message;
for(let i=0; i < message.length; i++) {
let value = 0;
for(let letter in shiftedAlphabets) {
if(letter === message[i]) {
value = shiftedAlphabets[letter]
break;
}
}
let key ='';
for(let letter in alphabets) {
if(alphabets[letter] === value) {
key = letter;
break;
}
}
newMessage = newMessage.replace(message[i], key)
}
return newMessage
}
//Big O: O(n²)
Shortcomings
- Alphabets are hard-coded: Almost every other solution I found uses a combination of String.prototype.charCodeAt() and String.fromCharCode()
This (hard-code) does not help in maintaining the original case. Capitalizing the first letter is not a solution as there can be random Capital letters inserted in the middle of the string.
This function returns the case as hardcoded in the initial objects. - This function does not fulfil the criteria of keeping the white-space, punctuations and symbols as is. Instead it returns the encrypted message without white space, punctuations and symbols which results in failure in passing the tests.
- This function has a time complexity of O(n²) (nested for loops) which could be better.
Steps
- Function encryptor declared with 2 parameters: key (the number by which alphabets are to be shifted) and message (string to be encrypted)
- Initialised 3 objects: alphabets (contains alphabet & number to represent the alphabetical order), shiftedAlphabets (contains alphabet & number added or subtracted by the key that is passed in ) and alphabetCap (contains alphabet & the maximum number that can be added to the original number to get 26).
- For...in loop through alphabetCap to subtract 26 from each alphabet number (this is the maximum number that can be added to the original number to get 26).
- Check if the input key is less than 26 and more than -26
- If Step 4 condition pass: for...in loop through shiftedAlphabets and add the key number with 2 conditions:
- 1st condition - If shiftedAlphabet value is more than 26:
This will reset the alphabet value starting it again from 1 after it crosses 26.let reset = Math.abs(alphabetCap[letter] - key); shiftedAlphabets[letter] = reset
- 2nd condition - if shiftedAlphabet value is less than 1:
This will reset the alphabet value starting it from 26 and backwards as it goes in the negative range.shiftedAlphabets[letter] = Math.abs(26 - Math.abs(shiftedAlphabets[letter]))
- Declare newMessage as a copy of input message and for...of loop through the message string.
- ~ Here we find the encrypted letter by referencing the two objects alphabets and shiftedAlphabets to replace each corresponding letter with corresponding encrypted key ~ This consists of 2 steps:-
- First Step - Inside the for...of loop init another for...in loop (in shiftedAlphabets) to search for the value of shifted key matching the alphabet.
- Second Step - Init key = '', and another for...in loop (in alphabets) to check if original alphabet value is equal to the value in Step 10 and return the letter in empty string key. ~This key is the shifted value and the previous steps perform the lookup for encrypted key value~
- Inside the for of loop (Step 8), each letter of the message is replaced with the key characters. Code:
newMessage = newMessage.replace(message[i], key)
- Return newMessage (Returns a string with encrypted letters however all whitespace is deleted and symbols, case and numbers are omitted from the return string.
Possible better solutions
- Using String.prototype.charCodeAt() and String.fromCharCode() to save me from the headache of trying to maintain the same case.
Things I found out
- You can declare other functions to help with the main solution function in codewars solution box.