JavaScript FreeCodeCamp Palindrome - javascript

I wrote the following code to check for a palindrome in JS. I can't get the following strings to work:
palindrome("My age is 0, 0 si ega ym.")
palindrome("A man, a plan, a canal. Panama")
I don't understand why. I understand there are more simple methods but I want to understand why this in particular isn't working. Here is the code:
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.replace(/[^a-z0-9]/g, '');
str = str.toLowerCase();
//turns string into an array of characters, including whitespaces
array = str.split("");
//removes white space in the array
array = array.filter(function(val) {
return /\S/.test(val);
});
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."))
console.log(palindrome("A man, a plan, a canal. Panama"))

You're eliminating UPPER CASE characters before you lower case the string.
For example: "A man, a plan, a canal. Panama"
str = str.replace(/[^a-z0-9]/g, '');
We now have: "manaplanacanalanama" - because you've said "kill of anything that isn't lower case or a number. Oops.
Change the order to this and it will work!
str = str.toLowerCase();
str = str.replace(/[^a-z0-9]/g, '');

Your first regex is case sensitive and only matches lowercase letters, so you are stripping out all the capital letters.
Fixed by adding one character:
(alternatively, you could move the .toLowerCase() line up before using the regex)
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.replace(/[^a-z0-9]/ig, '');
str = str.toLowerCase();
//turns string into an array of characters, including whitespaces
array = str.split("");
//removes white space in the array
array = array.filter(function(val) {
return /\S/.test(val);
});
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."));
console.log(palindrome("A man, a plan, a canal. Panama"));
console.log(palindrome('Ada'));
console.log(palindrome('Dada'));

You first remove non-lower-alphanumeric characters before you lower-case the string. Swap those two lines so you first convert the string to all lower-case characters, then remove non-alphanumerics.
Since you already removed characters that aren't a-z or 0-9, you don't have to account for whitespace at all! This means you can remove your filter(), which does nothing in this case.
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.toLowerCase();
str = str.replace(/[^a-z0-9]/g, '');
array = str.split("");
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."))
console.log(palindrome("A man, a plan, a canal. Panama"))

Move the string to lowercase above the replace and try again.
function palindrome(str) {
//gets rid of all special characters, turns string to lowercase
str = str.toLowerCase();
str = str.replace(/[^a-z0-9]/g, '');
//turns string into an array of characters, including whitespaces
array = str.split("");
//removes white space in the array
array = array.filter(function(val) {
return /\S/.test(val);
});
//defines new variable for the length of the array
var length = array.length;
//while loop to cycle through the array. Originally used zero, but I realized that wouldnt work for an odd-
// numbered array. takes off the first and last value and compares them. If they are not equal, returns
// false.
while (length > 1) {
var a = array.shift();
var b = array.pop();
length = array.length; //sets new length
if (a != b) {
return false;
}
}
return true;
}
console.log(palindrome("My age is 0, 0 si ega ym."))
console.log(palindrome("A man, a plan, a canal. Panama"))

Related

How can I mask the digits in a alphanumeric string when the count of digits reaches 10?

I have an alphanumeric string, so I want to mask all the numbers in this string when the count of digits reaches 10. In this example, the digit count has reached the count of 10 two times irrespective of how many space, special character,s or digits are there
For ex:
string 1:- abc23 56 dfg %#34567fhgh0 1234567890 abc345
Output:- abc** ** dfg %#*****fhgh* ********** abc345
It ignores the characters and mask the number when the digit length reaches 10. I want to do this with regex. How can I do that?
You may use something like this:
if ((s.match(/\d/g) || []).length >= 10) {
s = s.replace(/\d/g, '*');
}
This will count the number of digit matches. If there are 10 or more digits, it replaces each one with a '*' character. If you want to only replace the digits if the string contains at least one set of 10 consecutive digits, see the end of the answer.
Here's a complete example:
var arr = ['abc23 56 dfg %#34567fhgh0 1234567890 abc345', 'abc123def'];
for (var i = 0; i < arr.length; i++) {
let s = arr[i];
if ((s.match(/\d/g) || []).length >= 10) {
s = s.replace(/\d/g, '*');
arr[i] = s;
}
console.log(s);
}
Output:
abc** ** dfg %#*****fhgh* ********** abc***
abc123def
If you want the condition to be for 10 consecutive digits, use the following instead:
if (/\d{10}/g.test(s)) {
s = s.replace(/\d/g, '*');
}
You could split() the string into an array, check the length of the string, if it is over 10, then map the mask character where the number was using splice and its key along with Number and isNan.
var str = 'abc23 56 dfg %#34567fhgh0 1234567890'
var str2 = 'abc345'
var str3 = '%#34567fhg7'
var str4 = '1234567890'
const exchange = (str, cap) => {
// split the string and define array
const arr = str.split('')
// condtional to see if string.length is greater than cap
if (str.length > cap) {
// we have a string longer than cap, loop over the array, exclude
// empty spaces and check if value is a number using isNaN, if it is,
// we splice its value using the key with the mask character x
arr.map((char, k) => char !== ' ' ? Number.isNaN(Number(char)) ? null : arr.splice(k, 1, 'x') : null)
// return the value as a string by joining the array values together
return arr.join('')
} else {
// return the string as its length is not more than cap
return str
}
}
console.log(`${exchange(str, 10)}, length = ${str.length}`)
console.log(`${exchange(str2, 10)}, length = ${str2.length}`)
console.log(`${exchange(str3, 10)}, length = ${str3.length}`)
console.log(`${exchange(str4, 10)}, length = ${str4.length}`)

Calculate an alphabetic score for a word

How can I generate a numeric score for a string, which I can later user to order things alphabetically?
(I'd like to add objectIds to a redis sorted set based on a name property. This needs a numeric score. My list-of-things might be too big to sort all at once, hence wanting to score each item individually)
Words earlier in an alphabetic list should have a lower score, with 'a' = 0.
My naive approach so far; (letter alphabetic position from Replace a letter with its alphabet position )
function alphaScoreString(inputString) {
let score = 0
inputString
.trim()
.toLowerCase()
.split('')
.map((letter, index) => {
const letterNumber = parseInt(letter, 36) - 10
if (letterNumber >= 0) {
score += letterNumber / (index + 1)
}
})
return score * 1000
}
This does not work, as
alphaScoreString('bb')
1500
alphaScoreString('bc')
2000
alphaScoreString('bbz')
9833.333333333334
You can see that 'bbz' has a higher score than 'bc', whereas it should be lower, as 'bbz' would come before 'bc' in an alphabetical list.
You can convert each character to its unicode (and ensure that every character is 4 digits by padding the string. e.g. "H" = 72 but is padded to 0072: Doing a word by word comparison, you can still determine the 'alphabetical order' of each string:
var instring = "Hello World";
var output = "";
for(i=0; i<instring.length;i++){
const newchar = String(instring.charCodeAt(i)).padStart(4, '0');
output = output.concat(newchar)
console.log(output);
}
Answer writen in python.
char_codex = {'a':0.01, 'b':0.02, 'c':0.03, 'd':0.04, 'e':0.05, 'f':0.06,
'g':0.07, 'h':0.08, 'i':0.09, 'j':0.10, 'k':0.11, 'l':0.12,
'm':0.13, 'n':0.14, 'o':0.15, 'p':0.16, 'q':0.17, 'r':0.18,
's':0.19, 't':0.20, 'u':0.21, 'v':0.22, 'w':0.23, 'x':0.24,
'y':0.25, 'z':0.26}
def alphabetic_score(word):
bitwiseshift = '1'
scores = [0.00] * len(word)
for index, letter in enumerate(word.lower()):
if index is 0:
scores[index] = char_codex[letter]
else:
bitwiseshift = bitwiseshift+'00'
scores[index] = char_codex[letter]/int(bitwiseshift)
return sum(scores)

string compression counting the repeated character in javascript

If I have a string a12c56a1b5 then out put should be a13b5c56 as character a is repeated twice so a12 becomes a13
I have tried this:
function stringCompression (str) {
var output = '';
var count = 0;
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
It is happening because the code is counting the occurrence of each element and appending it, even the numbers in the string.
In this code,
for (var i = 0; i < str.length; i++) {
count++;
if (str[i] != str[i+1]) {
output += str[i] + count;
count = 0;
}
}
in first iteration i = 0, str[i] = 'a' and str[i + 1] = '1' for the given string a12c56a1b5 which are not equal hence, it will generate the output as a1 for first iteration, then a111 for second iteration since str[i] = '1' and str[i + 1] = '2' now, and so on.
We can achieve this by first separating the characters from the count. Assuming, that there would be characters from a-z and A-Z only followed by the count. We can do something like this, str.match(/[a-zA-Z]+/g) to get the characters: ["a", "c", "a", "b"] and str.match(/[0-9]+/g) to get their counts: ["12", "56", "1", "5"], put them in an object one by one and add if it already exists.
Something like this:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')
Using regex to extract word characters and numbers. Keeps an object map res to track and sum up following numbers. sorts and converts back to a string.
As an example, the for-of loop iteration flow with str=a12c56a1b5:
c='a', n='12'
res['a'] = (+n = 12) + ( (res['a'] = undefined)||0 = 0)
or ie: res['a'] = 12 + 0
c='c', n='56'
res['c'] = 56 + 0
c='a', n='1'
res['a'] = 1 + (res['a'] = 12 from iteration 1.) = 13
c='b', n='5'
res['b'] = 5 + 0
thus res = { 'a': 13, 'c': 56, 'b': 5 } after the for-of loop finishes
function stringCompression (str) {
// build object map with sums of following numbers
const res = {}
for(const [,c,n] of str.matchAll(/(\w+)(\d+)/g))
res[c] = +n + (res[c]||0)
// convert object map back to string
output = Object.entries(res)
output.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
output = output.map(([a,b])=>`${a}${b}`).join('')
console.log(output); // but it returns `a11121c15161a111b151` instead of `a13b5c56`
}
stringCompression('a12c56a1b5')
[,c,n] = [1,2,3] is equivalent to c=2, n=3. It is called destructuring.
matchAll matches on a regex. It's a relatively new shorthand for calling .exec repeatedly to execute a regular expression that collects all the results that the regular expression matches on.
(\w+)(\d+) is a regex for two capture groups,
\w+ is for one or more alpha characters, \d+ is for one or more digits.
for(const [,c,n] of str.matchAll...) is equivalent to:
for each M of str.matchAll...
const c = M[1], n = M[2]`
res[c]||0 is shorthand for:
"give me res[c] if it is truthy (not undefined, null or 0), otherwise give me 0"
+n uses the unary operator + to force an implicit conversion to a number. JavaScript specs for + unary makes it convert to number, since + unary only makes sense with numbers.
It is basically the same as using Number(n) to convert a string to an number.
Conversion back to a string:
Object.entries converts an object {"key":value} to an array in the form of [ [key1, value1], [key2, value2] ]. This allows manipulating the elements of an object like an array.
.sort sorts the array. I destructured the keys to sort on the keys, so "a" "b" "c" are kept in order.
.map takes an array, and "maps" it to another array. In this case I've mapped each [key,value] to a string key+value, and then taking the final mapped array of key+value strings and joined them together to get the final output.
In case it asks you to sort it alphabetically, I added #user120242's sorting code snippet to #saheb's entire answer (in between Object.keys(countMap) and .map(...). That worked for me. I tried using #user120242's whole answer, but it did not pass all the tests since it did not add the repeated letters for longer strings. But #user120242's answer did work. It just need to be sorted alphabetically and it passed all the test cases in HackerRank. I had this question for a coding assessment (called "Better Coding Compression").
P.S. I also removed checking the capital letters from #saheb's code since that wasn't required for my coding challenge.
Here's how mine looked like:
function stringCompression(str) {
var characters = str.match(/[a-zA-Z]+/g);
var counts = str.match(/[0-9]+/g);
var countMap = {};
for (var i = 0; i < characters.length; i++) {
if (countMap[characters[i]]) {
countMap[characters[i]] += parseInt(counts[i]);
} else {
countMap[characters[i]] = parseInt(counts[i]);
}
}
var output = Object.keys(countMap)
.sort(([a],[b])=>a<b ? -1 : a>b ? 1 : 0)
.map(key => key + countMap[key])
.reduce((a, b) => a + b);
console.log(output);
}
stringCompression('a12c56a1b5')

Finding twice the same random pattern

How to find a character only if it is always followed by the same letter (random and unknown)?
For example:
kelmqaetrstaeiii
E.g with the letter a, it is twice followed by the same letter e, so i would like it to be found, but with the following:
kelmqaetrstafiii
We have ae and af, 2 differents letters after a, in this case nothing should be taken.
Is it possible?
You could iterate over the string and check if one of the pairs already appeared in the string:
function hasPair(str) {
const dupe = {};
for (let index = 0; index < str.length - 1; index++) {
const pair = str.substr(index, 2);
if (dupe[pair])
return pair;
dupe[pair] = true;
}
return false;
}
console.log(
hasPair("kelmqaetrstaeii"),
hasPair("kelmqaetrstaii")
);
Match the first letter, then use lookahead, match the second letter, and use backreferences:
const re = /(\w)(?=(\w).+\1\2)/;
console.log('kelmqaetrstaeiii'.match(re)[0]);
console.log('kelmqaetrstafiii'.match(re));

How to check if a digit is used in a number multiple times

Example: We have the number 1122. I would like to check that if given number contains the digit 1 more than once. In this case, it should return true.
I need the code to be flexible, it has to work with any number, like 3340, 5660, 4177 etc.
You can easily "force" JS to coerce any numeric value to a string, either by calling the toString method, or concatenating:
var someNum = 1122;
var oneCount = (someNum + '').split('1').length;
by concatenating a number to an empty string, the variable is coerced to a string, so you can use all the string methods you like (.match, .substring, .indexOf, ...).
In this example, I've chosen to split the string on each '1' char, count and use the length of the resulting array. If the the length > 2, than you know what you need to know.
var multipleOnes = ((someNum + '').split('1').length > 2);//returns a bool, true in this case
In response to your comment, to make it flexible - writing a simple function will do:
function multipleDigit(number, digit, moreThan)
{
moreThan = (moreThan || 1) + 1;//default more than 1 time, +1 for the length at the end
digit = (digit !== undefined ? digit : 1).toString();
return ((someNum + '').split(digit).length > moreThan);
}
multipleDigit(1123, 1);//returns true
multipleDigit(1123, 1, 2);//returns false
multipleDigit(223344,3);//returns 3 -> more than 1 3 in number.
Use javascript's match() method. Essentially, what you'd need to do is first convert the number to a string. Numbers don't have the RegExp methods. After that, match for the number 1 globally and count the results (match returns an array with all matched results).
​var number = 1100;
console.log(number.toString().match(/1/g).length);​
function find(num, tofind) {
var b = parseInt(num, 10);
var c = parseInt(tofind, 10);
var a = c.split("");
var times = 0;
for (var i = 0; i < a.length; i++) {
if (a[i] == b) {
times++;
}
}
alert(times);
}
find('2', '1122');
Convert the number to a string and iterate over it. Return true once a second digit has been found, for efficiency.
function checkDigitRepeat(number, digit) {
var i, count = 0;
i = Math.abs(number);
if(isNaN(i)) {
throw(TypeError('expected Number for number, got: ' + number));
}
number = i.toString();
i = Math.abs(digit);
if(isNaN(i)) {
throw(TypeError('expected Number for digit, got: ' + digit));
}
digit = i.toString();
if(digit > 9) {
throw(SyntaxError('expected a digit for digit, got a sequence of digits: ' + digit));
}
for(i = 0; i < number.length; i += 1) {
if(number[i] === digit) {
count += 1;
if(count >= 2) { return true; }
}
}
return false;
}
In the event that you want to check for a sequence of digits, your solution may lie in using regular expressions.
var myNum = '0011';
var isMultipleTimes = function(num) {
return !!num.toString().match(/(\d)\1/g);
}
console.log(isMultipleTimes(myNum));
JavaScript Match
Using #Aspiring Aqib's answer, I made a function that actually works properly and in the way I want.
The way it works is:
Example execution: multDig('221','2')
Split the number (first argument) to an array where each element is one digit.Output: ['2','2','1']
Run a for loop, which checks each of the array elements if they match with the digit (second argument), and increment the times variable if there is a match.Output: 2
Check inside the for loop if the match was detected already to improve performance on longer numbers like 2211111111111111
Return true if the number was found more than once, otherwise, return false.
And finally the code itself:
function multDig(number, digit){
var finalSplit = number.toString().split(''), times = 0;
for (i = 0; i < finalSplit.length; i++){
if (finalSplit[i] == digit){
times++
}
if (times > 1){
return true;
}
}
return false;
}

Categories