2

I'm doing a kata that decodes a caesar cipher string into readable text. I'm using RegEx within a map to find special characters and skip over them, but the output is flaky if I have two or more special characters next to each other ', ' or ' :) '. It seems to skip over some special characters.

Can anyone explain what's going on?

I haven't included the changeCharCode function code because I think the issue is in my map.

function decodeString(string) {
  const stringArr = string.toLowerCase().split('');
  const specialCharacters = /[ .,\/#!$%\^&\*;:{}=\-_`~()]/g;
  const codeOfX = 'x'.charCodeAt(0);
  const codeOfLastLetter = stringArr[stringArr.length - 1].charCodeAt(0);
  const codeShift = codeOfX - codeOfLastLetter;

  return stringArr.map((elem) => {
    // Special character treatment
    return specialCharacters.test(elem) === true ? elem : changecharCode(elem, codeShift);
}).join('').toUpperCase();
  }

function changecharCode (letter, codeShift) {
  const currentCode = letter.charCodeAt(0);
  // Uppercase letters
  if ((currentCode >= 65) && (currentCode <= 90))
    return letter = String.fromCharCode(((currentCode - 65 + codeShift) % 26) + 65);
  // Lowercase letters
  else if ((currentCode >= 97) && (currentCode <= 122))
    return letter = String.fromCharCode(((currentCode - 97 + codeShift) % 26) + 97);
}

decodeString(' :) ') => ' ) '
decodeString(', ') => ','
alanionita
  • 1,272
  • 1
  • 16
  • 34
  • `specialCharacters.test` will be true for each character you have provided in the two calls, as you can easily verify with debugging. What do you mean with *"skip over"*? We cannot test the output without the `changecharCode` function. Could you provide it? – trincot Aug 26 '17 at 10:52
  • Added the code you for changecharCode. By 'skip over' I expect the map to return the element if it's a special character, so what when you join the mapped array the special characters are in the same locations. I also don't want to handle the special character in the changecharCode function. Yes you're right, the test calls for each output returns true, but as you can see from the output, the result of the map doesn't include all of the elements that are special characters. – alanionita Aug 26 '17 at 11:26
  • So indeed, it turns out your error is in that function. See my answer. – trincot Aug 26 '17 at 12:03

3 Answers3

2

Remove the global flag at the end of regex, you have to proceed one character at a time:

function decodeString(string) {
  const stringArr = string.toLowerCase().split('');
  const specialCharacters = /[ .,\/#!$%\^&\*;:{}=\-_`~()]/;
  //                                              here ___^
  const codeOfX = 'x'.charCodeAt(0);
  const codeOfLastLetter = stringArr[stringArr.length - 1].charCodeAt(0);
  const codeShift = codeOfX - codeOfLastLetter;

  return stringArr.map((elem) => {
    // Special character treatment
    return specialCharacters.test(elem) === true ? elem : changecharCode(elem, codeShift);
}).join('').toUpperCase();
  }

function changecharCode (letter, codeShift) {
  const currentCode = letter.charCodeAt(0);
  // Uppercase letters
  if ((currentCode >= 65) && (currentCode <= 90))
    return letter = String.fromCharCode(((currentCode - 65 + codeShift) % 26) + 65);
  // Lowercase letters
  else if ((currentCode >= 97) && (currentCode <= 122))
    return letter = String.fromCharCode(((currentCode - 97 + codeShift) % 26) + 97);
}

console.log('>'+decodeString(' :) ')+'<');
console.log('>'+decodeString(', ')+'<');
Toto
  • 89,455
  • 62
  • 89
  • 125
0

It's the same issue as why-does-my-javascript-regex-test-give-alternating-results. Let's say that the problem was occurred because of g flag which keeps a global state of matching. The solution in your case is supposed to be removing a g flag, once your function proceeds characters one by one, then the g flag is unnecessary.

fronthem
  • 4,011
  • 8
  • 34
  • 55
  • I see that you just deleted [\[ this \]](https://stackoverflow.com/questions/47495417/shell-script-check-if-data-in-columns-x-from-two-csv-files-are-matched) question which was tagged `bash`. Would you mind reopening as I have wrote a solution for that.? – sjsam Nov 26 '17 at 12:27
0

The problem is in your changecharCode function: if a character code is not within the two ranges that are tested, then the function returns nothing, i.e. undefined. The join you do later on will produce an empty string for each undefined value, so you don't see anything for that in the output.

If you would add a final to changecharCode:

return ' '; // or whatever character you want here

Then the output will have the same number of characters as the input.

trincot
  • 317,000
  • 35
  • 244
  • 286