[Message Mixer] Problem decoding message with caesarCipher when using a negative number

Exercise: Message Mixer

Hi everyone,

I am having issues decoding the message while using the caesarCipher() when the second argument is a negative integer.

const {caesarCipher, symbolCipher, reverseCipher} = require('./encryptors.js');

const encodeMessage = (str) => {
  // Use the encryptor functions here.
  return caesarCipher(str, amount=1);

}

const decodeMessage = (str) => {
  // Use the encryptor functions here.
  
  return caesarCipher(str, (-1));
  //return reverseCipher(symbolCipher(str));

}

The encodeMessage() and decodeMessage() work fine while using the other methods: symbolCipher() and reverseCipher(), which would suggest that the modules were correctly imported, so this “ReferenceError: caesarCipher is not defined” is puzzling me.

Any hints would be much appreciated,
Cheers

p.s : this is my first time posting here, so please thank you for your understanding in case of any formatting flaws

$ node super-encoder.js encode
Enter the message you would like to encrypt...
> hello world
ifmmp xpsme
$ node super-encoder.js decode
Enter the message you would like to encrypt...
> ifmmp xpsme
/home/ccuser/workspace/modules-message-mixer/encryptors.js:7
    return caesarCipher(str, amount + 26);
           

ReferenceError: caesarCipher is not defined
    at module.exports.caesarCipher (/home/ccuser/workspace/modules-message-mixer/encryptors.js:7:12)
    at decodeMessage (/home/ccuser/workspace/modules-message-mixer/super-encoder.js:13:10)
    at ReadStream.handleInput (/home/ccuser/workspace/modules-message-mixer/super-encoder.js:27:14)
    at emitOne (events.js:96:13)
    at ReadStream.emit (events.js:191:7)
    at readableAddChunk (_stream_readable.js:178:18)
    at ReadStream.Readable.push (_stream_readable.js:136:10)
    at TTY.onread (net.js:561:20)
$

This is how the three different files look like:

encryptor.js

// Declare and export the functions here.

// Encryption Functions
/////////////////////////////////////////////
module.exports.caesarCipher = (str, amount = 0) => {
  if (amount < 0) {
    return caesarCipher(str, amount + 26);
  }
  let output = '';
  for (let i = 0; i < str.length; i++) {
    let char = str[i];
    if (char.match(/[a-z]/i)) {
      let code = str.charCodeAt(i);
      if (code >= 65 && code <= 90) {
        char = String.fromCharCode(((code - 65 + amount) % 26) + 65);
      } else if (code >= 97 && code <= 122) {
        char = String.fromCharCode(((code - 97 + amount) % 26) + 97);
      }
    }
    output += char;
  }
  return output;
};

module.exports.symbolCipher = (str) => {
  const symbols = {
    'i': '!',
    '!': 'i',
    'l': '1',
    '1': 'l',
    's': '$',
    '$': 's',
    'o': '0',
    '0': 'o',
    'a': '@',
    '@': 'a',
    'e': '3',
    '3': 'e',
    'b': '6',
    '6': 'b'
  }

  let output = '';
  for (let i = 0; i < str.length; i++) {
    let char = str.toLowerCase()[i];

    if (symbols[char]) {
      output += symbols[char]
    } else {
      output += char;
    }
  }
  return output;
}

module.exports.reverseCipher = (sentence) => {
  let words = sentence.split(' ');
  for (let i = 0; i < words.length; i++) {
    words[i] = words[i].split('').reverse().join('');
  }
   return words.join(' ');
};```


**message-mixer.js**

// Import the functions from encryptors.js here.

const encryptors = require(‘./encryptors.js’);

const {caesarCipher, symbolCipher, reverseCipher} = encryptors;
// User Input / Output Logic
/////////////////////////////////////////////

const encryptionMethod = getEncryptionMethod();
process.stdin.on(‘data’, (userInput) => {
displayEncryptedMessage(encryptionMethod, userInput);
});

/* Helper function for determining which cipher method
the user chose when they ran the program. */
function getEncryptionMethod() {
let encryptionMethod;

const encryptionType = process.argv[2];
if (encryptionType === ‘symbol’) {
encryptionMethod = symbolCipher;
} else if (encryptionType === ‘reverse’) {
encryptionMethod = reverseCipher;
} else if (encryptionType === ‘caesar’) {
let amount = Number(process.argv[3]);
if (Number.isNaN(amount)) {
process.stdout.write(Try again with a valid amount argument. \n)
process.exit();
}
encryptionMethod = (str) => caesarCipher(str, amount);
}
else {
process.stdout.write(Try again with a valid encryption type. \n)
process.exit();
}

process.stdout.write('Enter the message you would like to encrypt…\n> ');
return encryptionMethod;
}

/* Helper function for displaying the encrypted message to the user. */
function displayEncryptedMessage(encryptionMethod, userInput) {
let str = userInput.toString().trim();
let output = encryptionMethod(str);
process.stdout.write(\nHere is your encrypted message:\n> ${output}\n)
process.exit();
}


**super-encoder.js**

// Import the encryptors functions here.
const {caesarCipher, symbolCipher, reverseCipher} = require(‘./encryptors.js’);

const encodeMessage = (str) => {
// Use the encryptor functions here.
return caesarCipher(str, amount=1);

}

const decodeMessage = (str) => {
// Use the encryptor functions here.

return caesarCipher(str, (-1));
//return reverseCipher(symbolCipher(str));

}

// User input / output.

const handleInput = (userInput) => {
const str = userInput.toString().trim();
let output;
if (process.argv[2] === ‘encode’) {
output = encodeMessage(str);
}
if (process.argv[2] === ‘decode’) {
output = decodeMessage(str);
}

process.stdout.write(output + ‘\n’);
process.exit();
}

// Run the program.
process.stdout.write('Enter the message you would like to encrypt…\n> ');
process.stdin.on(‘data’, handleInput);

Hi,

  1. Your return statement for encode had “amount=1” while your return statement for decode simply had -1. Perhaps this is causing the error?

  2. Looking at the hint, the way the parenthesis is written in the encode and decode are different. I don’t know why this is, but if you follow the exact syntax, the program might work:

[codebyte]

decodevsencode

Howdy! I don’t know if you’ve solved this, but I also got stuck on these steps. I’ve figured out a solution that allows me to complete the challenge!

The “ReferenceError: caesarCipher is not defined” is stemming from the way you export caesarCipher. The function isn’t defined when it’s being called. In other terms, the function is calling itself before it has been defined.

The solution is to separately define your module exports at the end of encryptors.js. I’ve included my code at the end of my post.

Why this is… I don’t have a good answer for, but it works.

// encryptors.js
/////////////////////////////////////////////
const caesarCipher = (str, amount = 0) => {
  let output = '';
  if (amount < 0) {
    output = caesarCipher(str, (amount + 26));
    return output;
  }
  for (let i = 0; i < str.length; i++) {
    let char = str[i];
    if (char.match(/[a-z]/i)) {
      let code = str.charCodeAt(i);
      if (code >= 65 && code <= 90) {
        char = String.fromCharCode(((code - 65 + amount) % 26) + 65);
      } else if (code >= 97 && code <= 122) {
        char = String.fromCharCode(((code - 97 + amount) % 26) + 97);
      }
    }
    output += char;
  }
  return output;
};

const symbolCipher = (str) => {
  const symbols = {
    'i': '!',
    '!': 'i',
    'l': '1',
    '1': 'l',
    's': '$',
    '$': 's',
    'o': '0',
    '0': 'o',
    'a': '@',
    '@': 'a',
    'e': '3',
    '3': 'e',
    'b': '6',
    '6': 'b'
  }

  let output = '';
  for (let i = 0; i < str.length; i++) {
    let char = str.toLowerCase()[i];

    if (symbols[char]) {
      output += symbols[char]
    } else {
      output += char;
    }
  }
  return output;
}

const reverseCipher = (sentence) => {
  let words = sentence.split(' ');
  for (let i = 0; i < words.length; i++) {
    words[i] = words[i].split('').reverse().join('');
  }
   return words.join(' ');
};

module.exports = {
    caesarCipher,
    symbolCipher,
    reverseCipher
}
// super-encoder.js
const encryptors = require('./encryptors.js');
const { caesarCipher, 
        symbolCipher, 
        reverseCipher } = encryptors;

const encodeMessage = (str) => {
  return symbolCipher(reverseCipher(caesarCipher(str, 9)));
}

const decodeMessage = (str) => {
  return caesarCipher(reverseCipher(symbolCipher(str)), -9);
}

// User input / output.

const handleInput = (userInput) => {
  const str = userInput.toString().trim();
  let output;
  if (process.argv[2] === 'encode') {
    output = encodeMessage(str);
  } 
  if (process.argv[2] === 'decode') {
    output = decodeMessage(str);
  } 
  
  process.stdout.write(output + '\n');
  process.exit();
}

// Run the program.
process.stdout.write('Enter the message you would like to encrypt...\n> ');
process.stdin.on('data', handleInput);
2 Likes

I’ve resolved the issue. The syntax you’ve pointed out is correct within the scope of the lesson.

Thanks for the reply. Unfortunately, neither suggestion has worked so far. I completed the exercise with a simpler combination of encoding/decoding functions. :slight_smile:

Thanks for your input. I also tried replacing/adding an export statement at the end of encryptors.js without success. But, overall, I would say that the modules seem to be adequately exported and imported, as it works with simpler combinations of encoding/decoding functions.

Take a look at this specific part of my encryptors.js:

const caesarCipher = (str, amount = 0) => {
  let output = '';
  if (amount < 0) {
    output = caesarCipher(str, (amount + 26));
    return output;
  }
  for (let i = 0; i < str.length; i++) {
    let char = str[i];
    if (char.match(/[a-z]/i)) {
      let code = str.charCodeAt(i);
      if (code >= 65 && code <= 90) {
        char = String.fromCharCode(((code - 65 + amount) % 26) + 65);
      } else if (code >= 97 && code <= 122) {
        char = String.fromCharCode(((code - 97 + amount) % 26) + 97);
      }
    }
    output += char;
  }
  return output;
};

I redefined output equal caesarCipher adjustment, and then return the output variable.

I have a guess here, only because I didn’t run into this issue. I think it’s because you’re creating an anonymous function and then adding it to the export object vs creating a named function. The reason this causes an error I think, is because the name of the function is used IN the function itself (meant to be recursive). However, since you didn’t name the function (you just named an object property that you assigned the anonymous function to if I understand it correctly), it technically hasn’t been defined here. In the places where it is imported, it IS named, so you don’t have the issue there.

So instead of

module.exports.caesarCipher = (str, amount = 0) ...

Use const caesarCipher = (str, amount = 0) => {... and then separately add this function to the exports object property.

2 Likes

The best explanation!

it’s because you’re creating an anonymous function and then adding it to the export object vs creating a named function. The reason this causes an error I think, is because the name of the function is used IN the function itself (meant to be recursive).

I had the same problem. It seems my syntax was wrong. This is how I modify it. I hope it helps

const encodeMessage = (str) => {
  // Use the encryptor functions here.
 return reverseCipher(symbolCipher(caesarCipher(str, 6)));
};

const decodeMessage = (str) => {
  // Use the encryptor functions here.
  return reverseCipher(symbolCipher(caesarCipher(str, -6)));
};

Hmm, yes, this worked for me. I only needed to change caesarCipher’s declaration from

module.exports.caesarCipher = (str, amount) =>

{ ...

to

module.exports.caesarCipher = function caesarCipher(str, amount)

{ ...