Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JavaScript - Caesar Cipher

I know there have been a few posts about Caesar Ciphers in the past, which I have had a look at, but I haven't found an answer which has helped me to solve this kata, hence my post.

The language is JavaScript. I've written 3 tests, 2 of which are passing so far, but the third is not. I tried to use a nested for loop to loop over the alphabet and the str, and compare them, then shift the alphabet index up/down according to whatever the number was, then push that letter into a new array, and return the joined array at the end.

It's working for positive numbers, but not negatives. (I should also point out that I haven't thought of how to handle spaces yet, I just wanted to get it working for single words first, then take it from there, thanks!)

Any pointers in the right direction would be appreciated.

Kata Instructions:

The function caesarCipher should take a string and a number (n) and return a new string with a Caesar cipher applied. A Caesar cipher replaces each plaintext letter with a different one a fixed number of places up or down the alphabet. N represents the number of shifts up or down the alphabet should be applied. It may be negative or positive.

  E.g.
  caesarCipher('hello', 2)
    --> 'jgnnq'
  caesarCipher('hello world!', -3)
    --> 'ebiil tloia!'

My tests:

const caesarCipher = require("../katas/caesar-cipher");
const { expect } = require("chai");

describe.only("caesarCipher", () => {
  it("returns an empty string when passed an empty string", () => {
    const alphabet = [
      "a",
      "b",
      "c",
      "d",
      "e",
      "f",
      "g",
      "h",
      "i",
      "j",
      "k",
      "l",
      "m",
      "n",
      "o",
      "p",
      "q",
      "r",
      "s",
      "t",
      "u",
      "v",
      "w",
      "x",
      "y",
      "z"
    ];
    const str = "";
    const num = 2;
    const actualResults = caesarCipher(alphabet, str, num);
    const expectedResults = "";
    expect(actualResults).to.equal(expectedResults);
  });
  it("returns a string with the letters replaced by the number of shifts up the alphabet", () => {
    const alphabet = [
      "a",
      "b",
      "c",
      "d",
      "e",
      "f",
      "g",
      "h",
      "i",
      "j",
      "k",
      "l",
      "m",
      "n",
      "o",
      "p",
      "q",
      "r",
      "s",
      "t",
      "u",
      "v",
      "w",
      "x",
      "y",
      "z"
    ];
    const str = "hi";
    const num = 2;
    const actualResults = caesarCipher(alphabet, str, num);
    const expectedResults = "jk";
    expect(actualResults).to.equal(expectedResults);
  });
  it("returns a string with the letters replaced by the number of shifts down the alphabet", () => {
    const alphabet = [
      "a",
      "b",
      "c",
      "d",
      "e",
      "f",
      "g",
      "h",
      "i",
      "j",
      "k",
      "l",
      "m",
      "n",
      "o",
      "p",
      "q",
      "r",
      "s",
      "t",
      "u",
      "v",
      "w",
      "x",
      "y",
      "z"
    ];
    const str = "dog";
    const num = -3;
    const actualResults = caesarCipher(alphabet, str, num);
    const expectedResults = "ald";
    expect(actualResults).to.equal(expectedResults);
  });
});

My Solution:

function caesarCipher(alphabet, str, num) {
  const strToArray = str.split("");
  console.log(strToArray);
  const cipheredStr = [];
  for (let i = 0; i < strToArray.length; i++) {
    for (let j = 0; j < alphabet.length; j++) {
      if (strToArray[i] === alphabet[j] && Math.sign(num) === 1) {
        console.log(Math.sign(num));
        cipheredStr.push(alphabet[(j += num)]);
      } else if (strToArray[i] === alphabet[j] && Math.sign(num) === -1) {
        console.log(Math.sign(num));
        console.log(alphabet[(j -= num)]);
        cipheredStr.push(alphabet[(j -= num)]);
      }
    }
  }
  console.log(cipheredStr.join(""));
  return cipheredStr.join("");
}

The Results:

caesarCipher
[]

    ✓ returns an empty string when passed an empty string
[ 'h', 'i' ]
1
1
jk
    ✓ returns a string with the letters replaced by the number of shifts up the alphabet
[ 'd', 'o', 'g' ]
-1
g
-1
r
-1
j
jum
    1) returns a string with the letters replaced by the number of shifts down the alphabet


  2 passing (15ms)
  1 failing

  1) caesarCipher
       returns a string with the letters replaced by the number of shifts down the alphabet:

      AssertionError: expected 'jum' to equal 'ald'
      + expected - actual

      -jum
      +ald
      
      at Context.<anonymous> (spec/caesar-cipher.spec.js:108:30)
      at processImmediate (internal/timers.js:456:21)
like image 452
IndigoDreams Avatar asked Mar 08 '26 16:03

IndigoDreams


1 Answers

The problem is that when num is negative, you are doing the mapping:

cipheredStr.push(alphabet[(j -= num)]);

But num is negative. When you subtract a negative number what you are doing is adding its absolute value.

Instead, you should do:

cipheredStr.push(alphabet[j + num]);

Also, note that to compute the index in the alphabet you don't need to put the = in there.


Side notes

I understand that your solution is a work in progress. You have to take into consideration:

  • What happens when you sum j + num to do the translation and it goes out of boundaries of your alphabet. Same thing could happen with a negative value of num.
  • In the declaration of the problem, it states that caesarCipher should only accept two parameters, but you are passing the alphabet as first parameter!

Good luck with your code and keep trying :)

like image 63
mgarcia Avatar answered Mar 11 '26 04:03

mgarcia



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!