ROT13 Cipher JavaScript Solution

Welcome to day 3 of 365 days of coding! Today we are going to jump into ciphers. Specifically the ROT13 Caesar cipher.

Disclaimer: there are MANY ways to solve this problem these are a few answers that I would see or use in a coding interview and would accept as a proper answers

TLDR: explanation of best solution at the bottom of the post and actual solutions at the bottom of each section

What is a ROT13 Caesar Cipher?

If you have ever done any research on cyber security or cryptography you have probably heard of a Caesar cipher. If you haven’t, a Caesar cipher is a shift cipher that shifts all the letters of the alphabet a set number of letters down the alphabet. A ROT3 being the most common Caesar Cipher. A ROT13 means each letter substituted with the 13th letter after it in the alphabet

The Problem

Create a function that takes a string and returns the string with each letter substituted with the 13th letter after it in the alphabet (ROT13). If there are numbers or special characters included in the string, they should be returned as they are.

Examples:

    rot13("Hello Dev World is awesome!")   // Uryyb Qri Jbeyq vf njrfbzr
    rot13("my dogs are the cutest dogs in the world")   // zl qbtf ner gur phgrfg qbtf va gur jbeyq
    rot13("abcdefghijklmnopqrstuvwxyz")  // nopqrstuvwxyzabcdefghijklm
    rot13("#365DaysOfCoding") // #365QnlfBsPbqvat

Solutions

I ran all of the solutions in jsbench and they were all within a couple of percentage points each time. It wasn’t consistent which one was the fastest. This tells us there isn’t a significant performance difference between the different solutions.

so lets break down what we need to do

  • possibility 1

    • have a string of the alphabet

    • have a string of the alphabet ciphered

    • do a replace based of of indexes

  • possibility 2

    • have a string of alphabet

    • replace with index of the letter + 13

  • possibility 3

    • replace message with

      • get UTF code for letter

      • add 13 or subtract 13 from that code (add if before m subtract after m)

      • get string based off that code

Solution 1

First we need to create a function that accepts a message

const rot13 = (message) => {
    // have a string of the alphabet
    // have a string of the alphabet ciphered
    // do a replace based of of indexes
}

Now we need to set the alphabet as we know it to a variable. I have used const here because it will not be changing.

const rot13 = (message) => {
  const originalAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    // have a string of the alphabet ciphered
    // do a replace based of of indexes
}

We are going to do the same thing again but with a ciphered version of the alphabet.

const rot13 = (message) => {
    const originalAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    const cipher = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
    // do a replace based of of indexes
}

And finally we will use .replace() to swap out the letter from the message with the letter from the cipher by pulling the index of the letter we are on from the original alphabet and using that index to pull the letter from the same index in the cipher string. The .replace() will be put on the message so that it will do it for every letter in the passed in string. Since the regex is only checking letters all numbers, spaces, and punctuation will remain the same.

const rot13 = (message) => {
  const originalAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
  const cipher = "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM"
  return message.replace(/[a-z]/gi, letter => cipher[originalAlpha.indexOf(letter)])
}

Solution 2

For this solution we are going to again create a function and throw the alphabet in a variable

const rot13 = (message) => {
  const alpha = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLM';
  //replace with index of the letter + 13
}

This time the replace isn’t going to pull a

const rot13 = (message) => {
  const alpha = 'abcdefghijklmnopqrstuvwxyzabcdefghijklmABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLM';
  return message.replace(/[a-z]/gi, letter => alpha[alpha.indexOf(letter) + 13]);
}

Instead of having to add a variable for the ciphered alphabet we can just do that math in the replace.

Solution 3

Another clever solution I have seen before is the last solution. This solution is clever and is a 1 liner but it uses built in functions that aren’t common which can lead to readability issues.

function rot13(message) {
    return message.replace(/[a-z]/gi, letter => String.fromCharCode(letter.charCodeAt(0) + (letter.toLowerCase() <= 'm' ? 13 : -13)));
} 

Since this is a 1 liner I am just going to try and explain what it does rather than breaking it out. fromCharCode returns a string from a UTF-16 code. So every number between 0 and 65535 are tied to a string. charCodeAt returns the UTF-16 code that we are decoding with fromCharCode. the 0 that is passed to fromCharCode is an index and since we are only passing 1 letter the index will always be 0. We are doing + 13 if it below m because that will get us until x and then once we are at m we are at the middle and that is mapped to x so we need to start the alphabet over which means we need to get the code for the letters at the beginning of the alphabet instead so we need to go backwards instead of forwards. We are doing a regex replace so the letters get replaced by that returned letter.

Conclusion

The performance for all 3 solutions is negligible. The last solution is clever but not the easiest to understand. The other two are very similar but again there are a lot of ways to write solutions for this.

Please leave your solutions that you came up with in the comments section. If you have any challenge you would like to see done also leave that in the comments below you may see it come up! If you would like to get the challenge emailed to you every day in morning and a notification when the solution is posted subscribe below

Previous
Previous

Day 4 Challenge

Next
Next

Day 3 Challenge