Hi all! π
Together, let's walk through one possible solution for a common algorithm: the Palindrome Checker.
The Objective
Using Javascript, create a function that receives one argument, a string. The function should return true
if the string is a palindrome and return false
if the string is not a palindrome.
What is a Palindrome?
According to Merriam-Webster, a palindrome is:
any word, number, or phrase that reads the same backward or forward.
Some basic examples are: mom, civic, kayak, Hannah, and 1881. More complicated examples are phrases with spaces such as: "race car" and "taco cat". Even more complicated (and often silly) examples are phrases that ignore spacing and punctuation such as: "Yo, Banana Boy!", "A man, a plan, a canal, Panama!", and "Madam, in Eden, Iβm Adam."
So, how can we create a function that returns true for each of these cases?
Let's get started.
Writing our Test Cases
Before we begin, we'll write some test cases in order to check our code as we go using console.log()
.
In order to view the console output, install and run Node JS or use a sandbox like codesandbox.io.
Our tests:
console.log("Expecting: false");
console.log("Test 1:", isPalindrome('testing'))
console.log('Expecting: true');
console.log('Test 2:', isPalindrome('mom'))
console.log("Expecting: true");
console.log("Test 3:", isPalindrome("kayak"));
console.log("Expecting: true");
console.log("Test 4:", isPalindrome("1881"));
console.log("Expecting: true");
console.log("Test 5:", isPalindrome("Hannah"));
console.log("Expecting: true");
console.log("Test 6:", isPalindrome("race car"));
console.log("Expecting: true");
console.log("Test 7:", isPalindrome("A man, a plan, a canal, Panama!"));
console.log("Expecting: true");
console.log("Test 8:", isPalindrome(" Madam, in Eden, Iβm Adam."));
Writing our Function
We'll start with the basic function syntax, taking our string as an argument.
function isPalindrome(string) {
}
Next, using the spread operator, we'll spread our string into a new array and save this to a variable, stringArray
.
function isPalindrome(string) {
const stringArray = [...string]; //NEW!
}
We'll create a new empty array to hold our reversed string.
function isPalindrome(string) {
const stringArray = [...string];
const newArray = []; //NEW!
}
Then, using a for-each loop, we'll iterate over each index in our stringArray
and, using .unshift()
, add that element to the beginning of newArray
, effectively reversing the string.
function isPalindrome(string) {
const stringArray = [...string];
const newArray = [];
stringArray.forEach(element => { //NEW!
newArray.unshift(element); //NEW!
}); //NEW!
}
We can now call the .join()
method on newArray
, with an empty string as a parameter, in order to concatenate all the elements to form a new string. We'll save this to a variable reversedString
.
function isPalindrome(string) {
const wordArray = [...string];
const newArray = [];
wordArray.forEach(index => {
newArray.unshift(index);
});
const reversedString = newArray.join(''); //NEW!
}
To close out our function, we'll compare our original string
with the new reversedString
using the strict equality operator ===
. This will return a boolean, true
or false
. Include the return
statement in order to return the boolean, thus indicating whether the string is a palindrome or not.
function isPalindrome(string) {
const wordArray = [...string];
const newArray = [];
wordArray.forEach(index => {
newArray.unshift(index);
});
const reversedString = newArray.join(''); //NEW!
return string === reversedString; //NEW!
}
βοΈ But wait! βοΈ
When we check this against our tests, only tests 1 through 4 pass!
So far, our code does not account for capital letters, spacing, or punctuation.
In order for a string like Hannah to pass the test, we need to convert all of our letters to lowercase by calling the .toLowerCase()
method on our string. We then save it to the string
variable to update this value.
function isPalindrome(string) {
string = string.toLowerCase(); //NEW!
const wordArray = [...string];
const newArray = [];
wordArray.forEach(index => {
newArray.unshift(index);
});
const reversedString = newArray.join('');
return string === reversedString;
}
We now see that test 5 has passed!
To exclude punctuation and white-spaces, we can call the .replace()
method and use a regular expression pattern as the first parameter to identify these characters.
In our code, this method will look like: .replace(/\W/g, '')
Let's break this down:
RegEx patterns are enclosed in two slashes
//
.\W
is a RegEx pattern for "non-alphanumeric" characters.The
g
flag follows the RegEx expression and performs a global search, looking in the whole string and returning all matches.We set the second parameter of our
.replace()
method to an empty string in order to "erase" these characters from our updatedstring
.
When we put it all together, we get:
function isPalindrome(string) {
string = string.toLowerCase().replace(/\W/g, ''); //NEW!
const stringArray = [...string];
const newArray = [];
stringArray.forEach(index => {
newArray.unshift(index);
});
const reversedString = newArray.join('');
console.log(string);
return string === reversedString;
}
Finally, we run our tests againβ and voila! π
All 8 of our tests pass our Palindrome Checker π
Top comments (6)
Nice explanation. One thing that could be improved is your "tests": did you know about
console.assert(shouldBeTrue, "Error message if falsy")
? This works both in node and in the browser. Array.prototype.reverse() was already mentioned.Another classical approach would be a letter-wise comparison until the middle of the string with an early return:
>>
is the bitwise shift right operator, shifting all bits right once is the same as a floored division by two.Cool! Didn't know about console.assert() before. Thanks!
Since the underscore needs a special case, why is there no test for it?
Also:
\W
equates to[^A-Za-z0-9_]
- which also strips a lot of non-latin alphabet characters. If we consider that an acceptable restriction, using the more explicit/[^a-z0-9]/ig
(or/[^A-Za-z0-9]/g
) would seem to be a better option, rather than the special case for the underscore. (Besides, the final code snippet is, in fact, missing the underscore in the pattern ;) )For performance reasons we might also want to cache the compiled regex.
You're right!! To simplify and keep consistent, I'll just remove the check for underscore entirely, but thanks for the good tips!
Really well explained example. A great introduction to string and array manipulation for beginners, and some regexp for good measure!
For the sake of completeness, and also to offer a more concise option for the solution, you may want to also show an alternate version which uses string.split() and array.reverse() β which would also be nice way to demonstrate chaining functions
Thanks for the feedback! You're right, maybe I'll make another post using an alternative solution.