Palindrome Scanner Using JavaScript

Palindrome Scanner Using JavaScript

I've been learning a bit of regular expressions this week, and, as a random activity decided to do a bit of research on palindromes having heard of them a while back.

Fascinated with them, I thought of creating a function that could search for palindromes from inputs in JavaScript while applying the knowlegde acquired from my regex journey. This post is a side effect of that.

Before getting to the coding part, let's first get definitions and a bit of history on palindromes out of the way.

What are palindromes?

Palindromes are words, numbers, phrases, or sentences that when arranged backwards retain their character sequence. With sentences, everything else besides letters isn't taken into account, so long as the letter sequence remains the same when it's reversed.

Numbers also make up palindromes in the form of dates such as 02/02/2020 and 01/01/1010. Like with sentences, the backslashes and hyphens aren't taken into account.

Simple examples of palindromic words we daily use in english are eye, mum, dad, madam, deified e.t.c. Some sentence examples are "Never odd or even" and "Madam, I'm Adam". An example of a longer English palindromes is "Doc, note: I dissent. A fast never prevents a fatness. I diet on cod".

One of the most fascinating palindromes I've come across is the Latin sentence in a grafitto found at Herculaneum (a city buried by ash in 79CE) called the Sator Square. It reads "Sator Arepo Tenet Opera Rotas" which translates to "The sower Arepo holds with effort the wheels". The first letters of each word in this palindrome form the first word, and that is true for the remaining four letters on forming the remaining four words. This palindrome can be arranged into a word square that reads the same in four diffetent ways horizontally left-right and right-left, and vertically up-down and down-up such that it's refered as palindromatic.

Having covered this short history on palindromes, let's get to doing what brought us here in the first place.

Creating the palindrome scanner

We want to build our scanner as quick as possible, hence we will avoid creating UIs just to be able to pass our input to our program, in turn we will be running our scanner on the terminal.

At this point I assume that you have nodejs installed in your system. If not, go ahead and do that, I'll wait.

Waiting gif

Well.

To facilitate getting user input we will be using the readline module. So let's go ahead and set it up.

const readline = require('readline');
const { stdin: input, stdout: output } = require('process');
const rl = readline.createInterface({
  input,
  output
})

Here, we are using the readline module's interface that allows us to read data from the process.stdin Readable stream.

Next, we'll print some info about our program, followed by listening to end of user input action such as when the Enter button is pressed. We will be doing this by listening to when the readline's line event is emitted.

console.log("*** Palindrome Scanner ***");
console.log("Enter a palindrome: ");
rl.on('line', (input) => {
  console.log(`${input} is${palindromeScanner(input) ? '' : ' not'} a palindrome`);
console.log("Enter a palindrome: ");
})
`

As you can see in the above code block, whenever the enter button is pressed, we run the input against the palindromeScanner function to determine whether the provided input is a palindrome or not.

Let's proceed to constructing our palindromeScanner function.

function palindromeScanner(txt){
  // function code
}

For readability add the function between the dependency declarations and the line event listener.

Properties of palindromes

We have to define the properties of palidromes so that we can set up constraints that our scanner can vet each provided input through.

At the heart of our palindrome scanner we need to check for two main things with some caveats. One, the input can be of a text or number nature but not both, and two, when we strip everything else from our input, the reversed letter or number sequence after striping the non-alphanumeric characters remains the same.

With number palindromes, they have to be valid dates in the format dd-mm-yyyy with hyphens or forward slashes but not both.

For letter palindromes they have to be at least three characters long.

We'll be adding the following code inside our palindromeScanner function block.

Starting with the first condition where the palindrome can either be numeric or made of letters but not both.

  if(/\d/.test(txt) && /[a-z]/gi.test(txt)) return false;

We have also declared that if it's a numeric palindrome it has to be a date and we don't expect to find letters in the provided input.

  let isDate = /(\d{1,2}[-\/]{1}){2}\d{4}/.test(txt) && !(/-/.test(txt) && /\//.test(txt));
  if(isDate && /[a-z]/gi.test(txt)) return false;

Next, we'll be checking for palindrome type specific conditions.

For date palindromes we have to see if they are valid dates. Since the date pattern validation above can validate dates with 00-00-0000, here we'll be checking if the dates and months values provided are valid.

let finalSequence = '';
if(isDate){
  let dates = txt.match(/(\d{1,2}[-\/]{1})/g).map(val => parseInt(val.replaceAll(/[-\/]/g, '')));
  if(dates[0] < 1 || dates[0] > 31 || dates[1] < 1 || dates[1] > 12) return false;
  finalSequence = txt.match(/\d/g).join('');
}

We are declaring the variable finalSequence above which will be taking the final number or letter sequence that will be tested at the end of the palindromeScanner function. We then proceed to checking if the dates and months are valid.

Next, we'll check if the input is a letter palindrome and if it satisfies the conditions we declared above.

  if(!isDate){
    let characters = txt.match(/[a-z]/gi);
    if(characters.length < 3) return false;
    finalSequence = characters.join('').toLowerCase();
  }

Finally, we compare the sequence of our final string (finalSequence) with its reverse sequence and return the Boolean as the result of our function.

 return finalSequence === finalSequence.split('').reverse().join('');

Bringing all this together we have the following code:

const readline = require('readline');
const { stdin: input, stdout: output } = require('process');
const rl = readline.createInterface({
  input,
  output
})

function palindromeScanner(txt){
  if(/\d/.test(txt) && /[a-z]/.test(txt)) return false;
  let isDate = /(\d{1,2}[-\/]{1}){2}\d{4}/.test(txt) && !(/-/.test(txt) && /\//.test(txt));
  if(!isDate && !/[a-z]/.test(txt)) return false;

  let finalSequence = '';

  if(isDate){
    let dates = txt.match(/(\d{1,2})[-\/]{1}/g).map(val => parseInt(val.replaceAll(/[/-]/g, '')));
    if(dates[0] > 31 || dates[0] < 1 || dates[1] < 1 || dates[1] > 12) return false;
    finalSequence = txt.match(/[\d]/g).join('');
  }

  if(!isDate){
    let characters = txt.match(/[a-z]/gi);
    if(characters.length < 3) return false;
    finalSequence = characters.join('').toLowerCase();
  }

  return finalSequence === finalSequence.split('').reverse().join('');
}

console.log("** Palindrome Scanner **");
console.log("Enter palindrome: ");
rl.on('line', (input) => {
  console.log(`${input} is${palindromeScanner(input) ? '' : ' not'} a palindrome`);
  console.log("Enter palindrome: ");
})

And that completes our task of creating a palindrome scanner using JavaScript.

Closing

If you are curious enough to throw in some crazy palindromic texts, you'll notice that our palindrome scanner only checks if the provided input has palindromic characteristics. Whether you are providing meaningful words, phrases, or sentences, that's up to you to decide.

Have fun creating interesting palindromes.