Question

I'm building a Caesar Cipher in javascript. It uses a randomly set variable called currentkey as the cipher's key. it can be a number from -25 to +25, skipping 0.

I can't figure out why the function returns undefined in the beginning of the string, or why it translates the same letter for as long as the string goes, or even why that letter doesn't get translated at all.

var currentkey = 5  //for example
function convertKey(str) {
    var o_text = str;
    var o_letters = o_text.split("");
    var alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','x','y','w','z']
    var c_text, _x = "";
    for (x in o_letters) {
        if (currentkey > 0) {
            _x = alphabet[alphabet.indexOf(x) + currentkey]
        } else {
            _x = alphabet[alphabet.indexOf(x) + (26 - currentkey)]
        }
        c_text = c_text + _x;
    }
    return c_text;
}

For example, running convertKey("example") returns undefinedeeeeeee (undefined + 7 times the first letter in "example").

Was it helpful?

Solution 2

The undefined is a result of not initializing c_text before you try concatenate it with _x.

The code only works on one letter because alphabet.indexOf(x) returns -1 (not found). When using o_text = "abc", x equals 0, 1, and 2. Thus, the index of 0, 1, or 2 in alphabet is nonexistent (indicated by the -1 result). You need to relate those numbers back to o_text by changing indexOf(x) to indexOf(o_text[x]). Also, to prevent going outside the bounds of your array, you need a modulus operation to wrap values greater than 26 (I've used alphabet.length to allow this code to work with other alphabets) back around into a valid region. Thus, the correct code would be as follows (note: I've moved 'w' in your array into its proper location alphabetically, as I assumed its location in your example was an error and not intentional):

var currentkey = 5  //for example
function convertKey(str) {
    var o_text = str;
    var o_letters = o_text.split("");
    var 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']
    var c_text="", _x = "";
    for (x in o_letters) {
        if (currentkey > 0) {
            _x = alphabet[(alphabet.indexOf(o_letters[x]) + currentkey) % alphabet.length]
        } else {
            _x = alphabet[(alphabet.indexOf(o_letters[x]) + (26 - currentkey)) % alphabet.length]
        }
        c_text = c_text + _x;
    }
    return c_text;
}

alert(convertKey('abcdefghijklmnopqrstuvwxyz'));

This alert's fghijklmnopqrstuvwxyzabcde

OTHER TIPS

Implementation the Caesar Cipher algorithm in JavaScript is very interesting due to the fact that JavaScripts lacks true modulo operator. The % is just the reminder of division. Read this article for more explanations.

However, you can easily define the modulo as a custom function and then proceed to the implementation of the Caesar Cipher - which is a very simple form of encryption, in which each letter in the original message is shifted to the left or right by a certain number of positions.

To decrypt the message we simply shift back the letters the same number of positions.

Example:

  • JAVASCRIPT becomes MDYDVFULSW if we shift all letters by 3 positions
  • MDYDVFULSW turns back to JAVASCRIPT if we shift back all letters by 3 positions.

If after shifting a letter goes outside the range of letters, then the letter is wrapped around in alphabet. Example: Letter Z becomes C if is shifted by 3 positions.

This “wrap-around” effect means use of modulo. In mathematical terms, the above can be expressed as this:

En(x) = (x + n) mod 26

Dn(x) = (x – n) mod 26

Trying to implement this algorithm in JavaScript without the use of a proper modulo operator will produce either incorrect results or a very cryptic and difficult to understand code.

By using a custom modulo function, the code expresses the mathematical equation identically:

// Function will implement Caesar Cipher to
// encrypt / decrypt the msg by shifting the letters
// of the message acording to the key
function encrypt(msg, key)
{
    var encMsg = "";

    for(var i = 0; i < msg.length; i++)
    {
        var code = msg.charCodeAt(i);

        // Encrypt only letters in 'A' ... 'Z' interval
        if (code >= 65 && code <= 65 + 26 - 1)
        {
            code -= 65;
            code = mod(code + key, 26);
            code += 65;
        }

        encMsg += String.fromCharCode(code);
    }

    return encMsg;
}

// Implement modulo by replacing the negative operand 
// with an equivalent positive operand that has the same wrap-around effect
function mod(n, p)
{
    if ( n < 0 )
        n = p - Math.abs(n) % p;

    return n % p;
}

Have fun!

Encrypt a few messages to try out the code. Remember: If you encrypt with a positive key, use the complementary negative key to decrypt it. You can also use this code to decode those ROT13 messages that appears everywhere on the web and newsgroups.

If you want to find out other ways of implementing modulo in JavaScript, please consult the article mentioned at the beginning of this post.

(1) You are not iterating through your array o_letters properly.

(2) You are going out-of-bounds for your array.

for (var i = 0; i < o_letters.length; i++) {
    _x = alphabet[(alphabet.indexOf(o_letters[i]) + currentkey + 26) % 26]
    c_text = c_text + _x;
}

Also, in your code, you don't need .split("") at all.

I would just do this:

var currentkey = 5  //for example
function convertKey(str) {
    var ret = '';
    for (var i = 0; i < str.length; i++) {
        ret += String.fromCharCode((str.charCodeAt(i) + currentKey + 26) % 26);
    }
    return ret;          
}

Or more succinctly (but less efficiently):

function convertKey(str) {
    return str.split('').map(function(c) {
       return String.fromCharCode((c.charCodeAt(0) + currentKey + 26) % 26);
    }).join('');       
}

// working at https://www.freecodecamp.org/challenges/caesars-cipher

function rot13(str){    

var res = [];

var currentPosition;

var shiftedPosition;

for (var i = 0; i<str.length; i++){
    currentPosition = str.charCodeAt(i);

    if (currentPosition<65 || currentPosition>90){
    res.push(String.fromCharCode(currentPosition));
    }

    shiftedPosition = str.charCodeAt(i) - 13;

    if (currentPosition>=65 && shiftedPosition <65){
    res.push(String.fromCharCode(91-(13-(currentPosition-65))));
    }

    if (currentPosition>=78 && currentPosition<=90){
    res.push(String.fromCharCode(shiftedPosition));
    }
  }
return res.join('');`enter code here`
}

// Change the inputs below to test

rot13("GUR DHVPX OEBJA QBT WHZCRQ BIRE GUR YNML SBK.");

Here's a more simple answer:

var rot = {
  'A': 'N',
  'B': 'O',
  'C': 'P',
  'D': 'Q',
  'E': 'R',
  'F': 'S',
  'G': 'T',
  'H': 'U',
  'I': 'V',
  'J': 'W',
  'K': 'X',
  "L": 'Y',
  'M': 'Z',
  'N': 'A',
  'O': 'B',
  'P': 'C',
  'Q': 'D',
  'R': 'E',
  'S': 'F',
  'T': 'G',
  'U': 'H',
  'V': 'I',
  'W': 'J',
  'X': 'K',
  'Y': 'L',
  'Z': 'M',
  ' ': ' ',
  '.': '.',
  '!': '!',
  '?': '?'
};

// Change the inputs below to test
rot13("SERR CVMMN!");
// FREE CODE CAMP

function rot13(str) {
  var index = [];
  for (var key in str) {
    for (var property in rot) {
      if (str[key] === property) {
        index.push(rot[property]);
      }
    }
  }
  return index.join('');
}

function rot13(str) {
const alphabet1 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXY';
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
return str
  .split('')
  .map(function(char) {  
    const pos = alphabet.indexOf(char);
    return pos >=0? alphabet1[pos+13] : char;     
  })
  .join('');
}
rot13("SERR PBQR PNZC");

  
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top