Sammy
Sammy

Reputation: 93

JS: how to shift each letter in the given string N places down in the alphabet?

how to shift each letter in the given string N places down in the alphabet? Punctuation, spaces, and capitalization should remain intact. For example if the string is "ac" and num is 2 the output should be "ce". What's wrong with my code? It converts letter to ASCII and adds given number then converts from ASCII to letter back. The last line replaces space.

function CaesarCipher(str, num) {

    str = str.toLowerCase();
    var result = '';
    var charcode = 0;

    for (i = 0; i < str.length; i++) {
        charcode = (str[i].charCodeAt()) + num;
        result += (charcode).fromCharCode();
    }
    return result.replace(charcode.fromCharCode(), ' ');

}

I'm getting

TypeError: charcode.fromCharCode is not a function

Upvotes: 9

Views: 35905

Answers (10)

Nima Jamalian
Nima Jamalian

Reputation: 11

The easiest way for me is below: (Code in JavaScript)

function caesarCipherEncryptor(string, key) {
    let output = "";
    for(let char of string){
        let newCode = char.charCodeAt() + key % 26;
        if(newCode <= 122){
                output += String.fromCharCode(newCode);
        } else {
            output += String.fromCharCode(96 + (newCode%122));
        }
    }
    return output;
}

Upvotes: 0

bharath muppa
bharath muppa

Reputation: 1079

One way of solving it as below.

Note: This includes rolling of small, capital and Numeric characters. special symbols renders as it is.

 function rotationalCipher(input, rotationFactor) {
      // Write your code here
      if (!input || rotationFactor < 0 || rotationFactor > 1000000) {
        return "";
      }

      let capitalLetters = [...Array(26).keys()].map((n) => n + 65);
      let smallLetters = [...Array(26).keys()].map((n) => n + 97);
      let numerics = [...Array(10).keys()].map((n) => n + 48);

      return input
        .split("")
        .map((char) => {
          let asciiCode = char.charCodeAt();

          if (asciiCode >= 97 && asciiCode <= 122) {
            let newAsciiCode = smallLetters.indexOf(asciiCode) + rotationFactor;
            return String.fromCharCode(smallLetters[Math.round(newAsciiCode % 26)]);
          } else if (asciiCode >= 65 && asciiCode <= 90) {
            let newAsciiCode = capitalLetters.indexOf(asciiCode) + rotationFactor;
            return String.fromCharCode(
              capitalLetters[Math.round(newAsciiCode % 26)]
            );
          } else if (asciiCode >= 48 && asciiCode <= 57) {
            let newAsciiCode = numerics.indexOf(asciiCode) + rotationFactor;
            return String.fromCharCode(numerics[Math.round(newAsciiCode % 10)]);
          } else {
            return char;
          }
        })
        .join("");
    }

    console.log(rotationalCipher("bB_1c", 10));
    console.log(rotationalCipher("bB_1c", 100));
    console.log(rotationalCipher("bB_1c", 9));
    console.log(rotationalCipher("bB_1cbB_1cbB_1cbB_1cbB_1c", 900));

Upvotes: 0

Ally Rippley
Ally Rippley

Reputation: 525

You need to pass an argument to the fromCharCode method using the String object. Try:

function CaesarCipher(str, num) {
    // you can comment this line
    str = str.toLowerCase();

    var result = '';
    var charcode = 0;

    for (var i = 0; i < str.length; i++) {
        charcode = (str[i].charCodeAt()) + num;
        result += String.fromCharCode(charcode);
    }
    return result;

}
console.log(CaesarCipher('test', 2));

I had to modify the return statement, because it was introducing a bug for me

Upvotes: 14

robjez
robjez

Reputation: 3788

One need to take into account fact of shifting last letters in alphabet back to beginning. Here is my take on that:

var input = "Caesar Cipher";
    
function CaesarCipher(str, num) {
    var alphabet = "abcdefghijklmnopqrstuvwxyz";
    var newStr = "";

    for (var i = 0; i < str.length; i++) {
        var char = str[i],
            isUpper = char === char.toUpperCase() ? true : false;

        char = char.toLowerCase();

        if (alphabet.indexOf(char) > -1) {
            var newIndex = alphabet.indexOf(char) + num;
            if(newIndex < alphabet.length) {
              isUpper ? newStr += alphabet[newIndex].toUpperCase() : newStr += alphabet[newIndex];
            } else {
              var shiftedIndex = -(alphabet.length - newIndex);
                isUpper ? newStr += alphabet[shiftedIndex].toUpperCase() : newStr += alphabet[shiftedIndex];
            }
        } else {
           newStr += char;
        }
    }
    return newStr;
}

console.log(CaesarCipher(input, 20));

JSFiddle

Upvotes: 4

AnonymousSB
AnonymousSB

Reputation: 3604

Tossing my hat into the fray here, as I just completed this same challenge over on HackerRank.

My solution is similar to the one proposed by @Alexa-905.

Objectives:

  • Rotate the alphabet by k, e.g., k = 3, a becomes d, z becomes c
  • Uppercase letters stay uppercase, same for lowercase
  • All other non [a-zA-Z] characters remain the same

Magic Numbers Explained:

  • 65-90 are the ranges for A-Z (uppercase letters)
  • 97-122 are the ranges for a-z (lowercase letters)
  • 26 is the number of letters in the alphabet

Solution:

function caesarCipher(s, k) {
    let result = '';

    for (let i = 0; i < s.length; i++) {
        const charCode = s.charCodeAt(i);

        if (
            (charCode < 65 || charCode > 122) ||
            (charCode > 90 && charCode < 97)
        ) {
            result += s[i];
        } else {
            let newCharCode = charCode + Math.ceil(k % 26);

            if (charCode >= 97 && newCharCode > 122) {
                newCharCode = newCharCode - 122 + 96;
            }
            if (charCode <= 90 && newCharCode > 90) {
                newCharCode = newCharCode - 90 + 64;
            }

            result += String.fromCharCode(newCharCode);
        }

    }

    console.log(result);
    return result
}

caesarCipher('F rpb Jxqe.zbfi(h%26) ql zrq altk lk olqxqflkp', 3);
caesarCipher('26 rb cqn wdvkna xo unccnab rw cqn juyqjknc', 17)
caesarCipher('65-90 mdq ftq dmzsqe rad M-L (gbbqdomeq xqffqde)', 534);
caesarCipher('97-122 kbo dro bkxqoc pyb k-j (vygobmkco voddobc)', 425974);
caesarCipher('Aopz jvkl ybuz ha 454,064 vwlyhapvuz wly zljvuk!', 19);
caesarCipher('myyux://oxujwk.htr/hfjxfw-nk-ax-wjljc/1', 141235435141);

Upvotes: 3

Alexa-905
Alexa-905

Reputation: 60

function caesarCipher(s, k) {
    var n = 26; // alphabet letters amount
    if (k < 0) {
        return caesarCipher(s, k + n);
    }
    return s.split('')
        .map(function (c) {
            if (c.match(/[a-z]/i)) {
                var code = c.charCodeAt();
                var shift = code >= 65 && code <= 90 ? 65 : code >= 97 && code <= 122 ? 97 : 0;
                return String.fromCharCode(((code - shift + k) % n) + shift);
            }
            return c;
        }).join('');
}

Use String.charCodeAt() to convert the English character to ASCII.

Use String.fromCharCode() to convert ASCII to English character.

Try caesarCipher("always-look-on-the-bright-side-of-life", 10) => "kvgkic-vyyu-yx-dro-lbsqrd-csno-yp-vspo"

caesarCipher("kvgkic-vyyu-yx-dro-lbsqrd-csno-yp-vspo", -10) => "always-look-on-the-bright-side-of-life"

Upvotes: 3

Andrew
Andrew

Reputation: 528

I know this is a bit old but I thought I'd give it a go. Here's my take on it.

It accounts for uppercase and lowercase. It can also shift either forwards or backwards any amount!

function shift(str, n) {
	var shifted = '';
	n = n%26;
	for (var i = 0; i < str.length; i++) {
		let code = str[i].charCodeAt();
    let capital = (code > 64 && code < 91) ? true : false;
    if (code < (capital?65:97) || code > (capital?90:122) || n == 0) {
      shifted += str[i];
      continue;
    }
    if (n > 0) {
      if (code > (capital?90:122)-n) {
  			code = n + code - 26;
  		} else {
  			code += n;
  		}
    } else {
      if (code < (capital?65:97)-n) {
  			code = code + n + 26;
  		} else {
  			code += n;
  		}
    }
		shifted += String.fromCharCode(code);
	}
	return shifted;
}

console.log(shift('Ebiil, Tloia!', 3));

Upvotes: 1

Akmal Salikhov
Akmal Salikhov

Reputation: 872

That's my solution

function rot13(str) {
  var result = str.split("")
                  .map(function(val) {
                    var letterKey = val.charCodeAt() - 13;
                    if(letterKey < 65){
                      letterKey = 90 - (65 - letterKey - 1);
                    }
                    console.log(letterKey);
                    return String.fromCharCode(letterKey);
                  })
                  .join("");
  return result;
}

Upvotes: 1

MyLittleDax
MyLittleDax

Reputation: 111

Try:

result += String.fromCharCode(charcode)

Taken from: http://www.w3schools.com/jsref/jsref_fromCharCode.asp

Upvotes: 0

linuxdan
linuxdan

Reputation: 4864

The fromCharCode function doesn't operate on strings, it operates on the global String object like so String.fromCharCode(65, 66, 67); // "ABC" ripped off straight from the docs.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/fromCharCode

Upvotes: 1

Related Questions