JavaScript Practice: Classes - Problem #3 Solution Bug

Problem

I don’t think the solution Codecademy provided for problem number 3 solves for the requirements they gave… am I crazy???

Requirement

A shift cipher takes a plain text message and shifts each letter forward in the alphabet by a given number. For example, a shift cipher with a shift of 1 would turn the string 'hello' to 'ifmmp'.

Create a class ShiftCipher that takes the numerical value of the shift as a constructor parameter. The class should have two methods:

  • encrypt: takes a plain text string and returns a capitalized string with each letter shifted forward in the alphabet based on the set shift value.
  • decrypt: takes an encrypted message and returns a lower case string with each letter shifted back in the alphabet based on the set shift value.
  • In both methods, any character outside the alphabet should remain the same.
  • But if a character is shifted outside the alphabet in either direction it should be wrapped around to the other side. For example, encrypting a y with a shift of 4 results in C and decrypting an A with a shift of 1 result in z.

Codecademy Provided Solution

// Write class below
class ShiftCipher {
    constructor(shift){
      this.shift = shift;
    }
    encrypt(plainString) {
      let encryptString = '';
      const tempString = plainString.toUpperCase();
  
      for (let i=0; i < tempString.length; i++) {
        let charNum = tempString.charCodeAt(i);
        
        if (charNum <= 90 && charNum >= 65) {
          charNum += this.shift;
          if (charNum > 90) {
            charNum -= 26;
          }
        }
        encryptString += String.fromCharCode(charNum);
      }
      return encryptString;
    }
  
    decrypt(encryptString) {
      let decryptString = '';
      const tempString = encryptString.toLowerCase();
  
      for (let i=0; i < tempString.length; i++) {
        let charNum = tempString.charCodeAt(i);
        
        if (charNum <= 122 && charNum >= 97) {
          charNum -= this.shift;
          if (charNum < 97) {
            charNum += 26;
          }
        }
        decryptString += String.fromCharCode(charNum);
      }
      return decryptString;
    }
  }

// Test it
let myString = 'I am a god';
console.log(`ORIGINAL STRING: ${myString}`);
const shiftCipher1 = new ShiftCipher(1);
let encryptedString = shiftCipher1.encrypt(myString);
console.log(`ENCRYPTED STRING: ${encryptedString}`);
// Should print "J bn b hpe"
// Actually prints "J BN B HPE"
let decryptedString = shiftCipher1.decrypt(encryptedString);
console.log(`DECRYPTED STRING: ${decryptedString}`);
// Should print "I am a god'
// Actually prints "i am a god"
console.log(`ARE THEY THE SAME? ${myString === decryptedString}`);

My Solution

… I actually think this solves for the requirements:

// function that returns whether a char code is associated with an upper case letter, lower case letter, or not a letter
function letterCase(charCode) {
    if (charCode >= 65 && charCode <= 90) {
        return 'upper';
    } else if (charCode >= 97 && charCode <= 122) {
        return 'lower';
    } else {
        return 'not a letter';
    }
}

// function that returns whether a char code is in one of the letter ranges
function isALetter(charCode) {
    if ((charCode >= 65 && charCode <= 90) ||
        (charCode >= 97 && charCode <= 122)) {
        return true;
    } else {
        return false
    }
}

function switchCase(charCode, charCase) {
  let newCharCode = 0;
  if (charCase = 'upper') newCharCode = charCode + 32;
  if (charCase = 'lower') newCharCode = charCode - 32;
  return newCharCode;
}

// define Shift Cipher class
class ShiftCipher {
    constructor(shift) {
        this.shift = shift;
    }

    encrypt(string) {
        // convert the string to lower case so the logic is easier
        let lowerString = string.toLowerCase();
        // create array to track shifted character codes
        let encryptedCharCodeArray = [];
        // iterate through each character in the string
        for (let i = 0; i < string.length; i++){
          let originalCharCode = string.charCodeAt(i);
          // console.log(`Original char code: ${originalCharCode}`);
          let originalCase = letterCase(originalCharCode);
          // console.log(`Original case: ${originalCase}`);
          let lowerCharCode = lowerString.charCodeAt(i);
          // console.log(`Lower char code: ${lowerCharCode}`);
          let newCharCode = 0;
        // Should not be shifting if it's not a letter
          if (!isALetter(originalCharCode)) {
            newCharCode = originalCharCode;
          } else {
        // Shifting logic
            newCharCode = lowerCharCode + this.shift;
            if (!isALetter(newCharCode)) {
        // If the shifted character code is not in the letter range, bring back into range
                if (newCharCode < 97) {
                    newCharCode = 122 - (96 - newCharCode);
                    if (originalCase == 'lower') newCharCode = switchCase(newCharCode, originalCase);
                }
                if (newCharCode > 122) {
                    newCharCode = 96 + (newCharCode - 122);
                    if (originalCase == 'lower') newCharCode = switchCase(newCharCode, originalCase);
                }
              } else {
                if (originalCase == 'upper') newCharCode = switchCase(newCharCode, 'lower');
              }
          }
          // console.log(`New char code: ${newCharCode}`);
          // console.log('---')
          encryptedCharCodeArray.push(newCharCode);
        }
        // return a new string using spread charCodes from encryptedCharCodeArray
        // console.log(encryptedCharCodeArray);
        return String.fromCharCode(...encryptedCharCodeArray);
    }

    decrypt(string) {
        // convert the string to lower case so the logic is easier
        let lowerString = string.toLowerCase();
        // create array to track shifted character codes
        let decryptedCharCodeArray = [];
        // iterate through each character in the string
        for (let i = 0; i < string.length; i++){
          let originalCharCode = string.charCodeAt(i);
          // console.log(`Original char code: ${originalCharCode}`);
          let originalCase = letterCase(originalCharCode);
          // console.log(`Original case: ${originalCase}`);
          let lowerCharCode = lowerString.charCodeAt(i);
          // console.log(`Lower char code: ${lowerCharCode}`);
          let newCharCode = 0;
        // Should not be shifting if it's not a letter
          if (!isALetter(originalCharCode)) {
            newCharCode = originalCharCode;
          } else {
            // Shifting logic
                newCharCode = lowerCharCode - this.shift;
                if (!isALetter(newCharCode)) {
            // If the shifted character code is not in the letter range, bring back into range
                    if (newCharCode < 97) {
                        newCharCode = 122 - (96 - newCharCode);
                        if(originalCase === 'lower') newCharCode = switchCase(newCharCode, originalCase);
                    }
                    if (newCharCode > 122) {
                        newCharCode = 96 + (newCharCode - 122);
                        if (originalCase == 'lower') newCharCode = switchCase(newCharCode, originalCase);
                    }
                  } else {
                    if (originalCase == 'upper') newCharCode = switchCase(newCharCode, 'lower');
                  }
              }
          // console.log(`New char code: ${newCharCode}`);
          // console.log('---')
          decryptedCharCodeArray.push(newCharCode);
        }
        // return a new string using spread charCodes from encryptedCharCodeArray
        // console.log(decryptedCharCodeArray);
        return String.fromCharCode(...decryptedCharCodeArray);
    }
}

// Test this
let myString = 'I am a god';
console.log(`ORIGINAL STRING: ${myString}`);
const shiftCipher1 = new ShiftCipher(1);
let encryptedString = shiftCipher1.encrypt(myString);
console.log(`ENCRYPTED STRING: ${encryptedString}`);
let decryptedString = shiftCipher1.decrypt(encryptedString);
console.log(`DECRYPTED STRING: ${decryptedString}`);
console.log(`ARE THEY THE SAME? ${myString === decryptedString}`);