DEV Community

Cover image for Public Solving: Caesar decipher in JavaScript
Chris Bongers
Chris Bongers

Posted on • Originally published at

Public Solving: Caesar decipher in JavaScript

Some kid elves are being naughty and sending themselves encrypted messages during elf class.

Santa has asked us to decrypt these messages to see what's happening.

The Caesar cipher is pretty easy to understand. It's basically the alphabet but offset with an x amount of characters.

For example:

// shift 1
Enter fullscreen mode Exit fullscreen mode

As you can see, we shift the letters up by the shift amount.

To decipher a message, we have to do the opposite.

Thinking about the solution

At first, I started thinking about providing the alphabet as the shifted version and then mapping using that.
But then I realized using the charCode might actually be a more straightforward solution.

We used the charCode for hacking Santa's password.

However, we should only match on a-z and A-Z. Capital and lowercase use a different charCode offset, so it's vital to distinguish between them.

Probably something that a regex and the replace function can help us with!

Building a Caesar decipher in JavaScript

As mentioned, we need to only replace a-z and A-Z, meaning all characters like ,.! etc. should remain.

For this, we can use a regex and call another actual shift function for each of the two matches.

return text
    .replace(/[a-z]/g, (char) => letterShift(char, shift, 97))
    .replace(/[A-Z]/g, (char) => letterShift(char, shift, 65));
Enter fullscreen mode Exit fullscreen mode

As you can see, the starting offset for a lowercase letter is 97, and for uppercase, it's 65.

This means the character code for a is 97.
And for A it's 65.

Now let's move on to making the actual letterShift function we declared.

const letterShift = (letter, shift, offset) => { }
Enter fullscreen mode Exit fullscreen mode

I'll break down each step of the following function and end with the complete function ready for use.

Let's take aol as our message. We know the offset here is 7.

The first thing we need to do is retrieve the char code of the letter we received.


// a = 97 
// o = 111
// l = 108
Enter fullscreen mode Exit fullscreen mode

The next step is to offset this character code with the shift.
In our case, the shift is 7, so we have to detract 7 from our character code.

letter.charCodeAt() + shift

// a = 90
// o = 104
// l = 101
Enter fullscreen mode Exit fullscreen mode

For those paying attention, you might have spotted an issue here.

90 is not a valid letter since it should be more than 97.
The ol is correct already.

To solve this, we need to add 26 to negative numbers.
This means any number negative should be at the end of the alphabet.

letter.charCodeAt() + shift + 26

// a = 116
// o = 130
// l = 127
Enter fullscreen mode Exit fullscreen mode

Now the a is fine, but the ol are wrong as they should not have been plussed.

To make a rock-solid solution, we can do the following:

  • letter char code
  • minus the offset (97 for lowercase or 65 for uppercase)
  • add the shift (-7 in our example)
  • plus the alphabet length (+26)
  • get the remainder of 26 (% 26)
  • and then re-add the offset again

This way, we adhere to negative numbers as well as positive ones.

Making the complete function look like this:

return String.fromCharCode(
    ((letter.charCodeAt() - offset + shift + 26) % 26) + offset

// a = 116 = t
// o = 104 = h
// l = 101 = e
Enter fullscreen mode Exit fullscreen mode

Now let's run our test to see if this works for all the test cases.

Tests turning greeen

We did it!

Would really love to hear what your solution would be to this puzzle. ๐Ÿ‘

Thank you for reading, and let's connect!

Thank you for reading my blog. Feel free to subscribe to my email newsletter and connect on Facebook or Twitter

Top comments (3)

lexlohr profile image
Alex Lohr

An interesting property of latin alphabetical characters is that their 5th bit (32) being set or unset is the only difference between a large or a small character and the 6th bit (64) is set for both. We can also employ binary operations like | and & to filter certain bits.

characterCode & 31 = 1 for 'A', 2 for 'B'...
characterCode & 96 = 64 for upper case, 96 for lower case

That allows us to create a letterShift function that returns a function that directly works as a callback to string. replace.

const letterShift = (shift) => (letter) => {
  const charCode = letter.charCodeAt(0)
  // 64 for upper case, 96 for lower case
  const casePart = charCode & 96
  // 1 for a, 2 for b, ...
  const charPart = charCode & 31
  // use modulo for wraparound
  const shiftedCharPart = ((charPart - 1 + shift) % 26) + 1
  return String.fromCharCode(casePart | shiftedCharPart)
Enter fullscreen mode Exit fullscreen mode

P.S. I wrote this on my cellphone, so there are probably a few typos in there.

dailydevtips1 profile image
Chris Bongers

That is amazing, actually didn't dive that deep into it!
So really glad to find out we can use it as a two part element

kirankamath96 profile image
Kiran Kamath

Hey , can you check my solution , some suggestions required,