The Caesar’s Cipher was a fun little challenge. It’s cool to code some code 🙂
The goal of this challenge: Write a function which takes a ROT13 encoded string as input and returns a decoded string.
The suggested methods are String.charCodeAt() and String.fromCharCode().
Best Solution
function rot13(str) { var decoded=""; for (var i in str) { if (str.charCodeAt(i) < 65 || str.charCodeAt(i) > 91) { // check if char is outside A-Z range decoded += str[i]; continue; } // chars A-M will loop back over Z, so you need to add 13(26-13) if (str.charCodeAt(i) < 78) { decoded += String.fromCharCode(str.charCodeAt(i) + 13); } // all remaining chars - N-Z else { decoded += String.fromCharCode(str.charCodeAt(i) - 13); } } return decoded; } // Change the inputs below to test rot13("GUR DHVPX OEBJA QBT WHZCRQ BIRE GUR YNML SBK.");
CodePen Example
See the Pen Bonfire: Caesar’s Cipher – Best Solution by Mike Doubintchik (@allurewebsolutions) on CodePen.
Code Breakdown
From Mozilla: The charCodeAt()
method returns the numeric Unicode value of the character at the given index. The static String.fromCharCode()
method returns a string created by using the specified sequence of Unicode values.
First we create an empty variable called decoded. This will allow us later to populate one decoded character at a time until we reach the solution.
Next we create a for loop. In this case, I used the shorthand notation of for(var i in str)
which is the equivalent of for(i = 0; i < str.length; i++)
. This will iterate through the whole encrypted input string.
Our first check is to see whether the character in the string is non-“A to Z,” such as a period or space. A had a charCode of 65 and Z of 90, so we check if our current character is outside the range of 65 – 90. If it is, we add it directly to our decoded string without any manipulation and continue running the loop.
Then we check if a character is A – M. The reason we have to do this is because we can’t just shift left 13 (subtract 13) for characters with values 65 – 77. For example let’s take the letter E. It has a charCode value of 69. If we subtract 13, that will be 56, which is the number 8. Obviously that doesn’t work. The solution is to loop back around from the end of the alphabet.
We could do this by trying to find the remainder once we shift left as much as possible. The math goes like this 90 – (13 – (69 – 65)). However, it’s easier just to loop the other way in the alphabet, by shifting right. So instead we would do 69 + 26 (letters of the alphabet) – 13 (left shift) or 69 + 13 (26-13).
Finally, we can shift left all the characters greater than charCode 77 by 13.
Each new character gets added to the decoded string with decoded+=
. In the end, we return our decoded string.
Alternative Solution (as suggested by Aman in the comments)
This solution uses the array.push() method instead of concatenating strings. It’s approximately 30% slower when running benchmarks.
function rot13(str) { var answer = []; for (var i in str) { if (str.charCodeAt(i) < 65 || str.charCodeAt(i) > 91) { answer.push(str[i]); continue; } else { if (str.charCodeAt(i) < 78) { answer.push(String.fromCharCode(str.charCodeAt(i) + 13)); continue; } else { answer.push(String.fromCharCode(str.charCodeAt(i) - 13)); continue; } } } return answer.join(""); }
Alternative Solution (as suggested by Jonnie in the comments)
This solution uses character mapping and it’s completely different from all the other solutions. To see an explanation of how this solution check out Jonnie’s comment below.
rot13 = m => m.split('') .map(b => { x = b.charCodeAt(); return x > 96 && x < 123 ? String.fromCharcode((x - 84) % 26 + 97) : x > 64 && x < 91 ? String.fromCharCode((x - 52) % 26 + 65) : b }) .join('');
Discussion
I would love to hear others’ solutions and discover better and cooler ways to solve these challenges. Please comment with your questions, suggestions, or anything you would like.
If you found my solution useful or learned something new from this blog post, please feel free add kudos inside the main chat of Free Code Camp: Thanks @allurewebsolutions
Really cool solution. However, you might want to make a slight change. In Js, strings are immutable – so any function you use to concatenate or replace a string, usually returns a NEW string altogether. That’s why I think using the “+=” operator to append characters to decoded could be suboptimal. Instead, if you add all these characters to an array (as you did in your previous solution) and simply join them in the end, it could improve the efficiency by a few increments. Here’s my code – I did incorporate some of your things though – like adding a continue statement at every execution of ‘push’ where it’s pointless to leave the loop running.
I’m getting an error on this line:
for(i=0, len=str.length; i 64) && (str.charCodeAt(i) < 91)) {
Can you please check your code?
I’d like to run a comparison of speed script: http://codepen.io/allurewebsolutions/pen/qbpZLp?editors=001
FYI, you can use the tags
pre
andcode
to format the code in your comments.Hi, just wanted to share my solution, where I push everything into an array, check and convert, and then use join to turn it all into a string:
@Jay – It’s a very elegant solution, thank you. Do you know how fast it performs when benchmarked?
Here is my functional solution, somewhat faster (8%) according to jsperf.
It looks like percent symbol is not accepted, actually a whole statement was skipped
It should be:
Thanks for your solution and thanks for noticing the issue with the “%” symbol. I wrapped your solution in “pre” tags and tried to insert the missing code. Did I do it correctly?
Can you give a little description of your solution and thought process on how you reached it? I would like to add it to the blog post.
Andy, thanks for your solution. However, I’m getting the following error when trying to run it
Hey Mike, Im not sure what happened with the code above I provided, but its syntax is completely wrong. I guess during the copying/paste and edit, something may have happened, This here is the right code I wanted to provide before. It was late night, probably running out of coffee then :).
Andy, it seems like WordPress comments are stripping out some of your code still. Can you share your code through a code snippet site such as: https://gist.github.com/
Then I will compare and try to fix WordPress.
Thanks!
Mike, indeed WP is chewing part of the code off. And I thought I did it..lol
Here it goes …
https://gist.github.com/andythedandyone/9b6a7e1650bf5e232bcf76064790a1b9
Really not sure why WordPress is stripping code from comments, but I fixed it manually.
Your code is interesting…using two arrays. How does the performance compare?
Im not sure, jsperf is not working for the moment, if someone can benchmark it, feel free. I don’t expect it to be the fastest, I thought it just to be a clean code and easy enough for beginners to understand. The option of going with two arrays is simply to strip the string into single numbers inside the array, from there adding or subtracting depending on which half the numbers falls at. And for the last, pour the {un cipher’d} numbers back to alpha into a new array to display.
It is very clean and easy to understand. Thank you for sharing your solution, Andy!
You can actually make this code a ton simpler by making a map:
That didnt copy well:
Lets try again?
Cool, that is much shorter. Can you walk us through the code?
yeah,
so the rot13 = m =>… is arrow function notation… you should look it up, its newer and not supported on older browsers..
but the code basically does this, it splits the string into an array and calls the map function. when calling map, you pass a function that will execute on all elements in the array. This is bit advanced, but it means you don’t need a loop.
The function that map runs basically converts the character to a character code, and then will change the code by 13 characters, for wrap around, i use a modulus to do this.
for example for lower lower case character a, the character code is 97 you add 13 to this for 110, then subtract 97, for 13 take the modulus 26 in case its over 26, and then adds back 97 to get to 110… this seems like a ton of extra steps that are not needed.. but when you get to a character in the 2nd half of the alphabet it fixes the wrap round issue.
example, lower case ‘z’ is 122, add 13 for 135, then subtract 97 for 38, this is over 26… so its not in the lower case alphabet, you mod this by 26 to get to 12, then add 97 to get to 109, which is the correct character ‘m’
this function got really messed up when i submitted my comment, it should really look like the following:
http://screencast.com/t/rnwiBPTI1n0
the function checks to see if the character code is lower, upper, or defualt (any other character)
finally, the join function just joins the new array that map returns.
For some odd reason, its cutting out a bit of code in the middle…
Maybe adding spaces to my code will fix this?
if not then reply to this and i can send you an email
For some reason WordPress cuts off certain characters in comments. I still haven’t figured out how to solve that problem. Can you please put your code into a Github gist and share the link? https://gist.github.com/
https://gist.github.com/anonymous/3f2b929b44c47bcd2e78db1e116a3bef
@Jonnie – you have a really cool solution to the Caesar’s cipher challenge. I’ve added your solution to the blog post 🙂
Thanks!