This is from LeetCode - Valid Palindrome.
Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases.
Note: For the purpose of this problem, we define empty string as valid palindrome.
while (regex.test(s[start])) {
start++;}
--> can't understand how it works, I understood only that s[start] is alphanumeric characters, it will be false
if (!s[start] || !s[end])
--> What does this mean?
Below is the whole code
var isPalindrome = function(s) {
let regex = /[\W]/;
let start = 0;
let end = s.length - 1;
while (start < end) {
// Moves front runner to next alphanumeric
while (regex.test(s[start])) {
start++;
}
// Moves back runner to next alphanumeric
while (regex.test(s[end])) {
end--;
}
// Above would run until null if there are no alphanumeric characters so return true
if (!s[start] || !s[end]) {
return true;
}
// Check if equal and return false if not
if (s[start].toLowerCase() != s[end].toLowerCase()) {
return false;
}
// If index values match continue the while loop
start++;
end--;
}
return true;
};
Please Advise!
Same as previous solution, plus:
+ removing spaces and symbols, except (A-Z,a-z,0-9)
+ lowercasing
const isPalindrome = function(str) {
const expr = /[\W_]/g;
const lowcaseStr = str.toLowerCase().replace(expr, '');
const reverseStr = lowcaseStr.split('').reverse().join('');
return lowcaseStr === reverseStr
};
This is heavily over-engineered. Why not just use one for loop with 2 counters, one starting at 0 and one at the last index and, while they aren't equal, check if the characters at those indices are the same. Or use built in functions like
function palindrome(str){ return str.split('').reverse().join('') == str}
Related
I have a javascript problem that I am very confused about
Write a function stringCase that takes a string with mixed uppercase and lowercase characters and return the string in either all uppercase or all lowercase depending on which letter case the string has more of. If the string has equal upper and lower case characters, convert the string to all lowercase. Do not include any loops, length property, or native methods (exception: toLowerCase() & toUpperCase() allowed).
I can immediately think of a few approaches to this problem, like using a loop to check each character, or using a replace method and regex and getting the length of the result. However, the prompt prohibits me from using loops, length property, and native methods.
Any help would be appreciated. Thank you in advance!
The trick here is to use recursion to do the loop. You can manually track the pointer and counts.
const stringCase = (s) => {
let i = 0;
let count = 0;
const process = () => {
const curr = s[i++];
if (!curr) {
return count <= 0 ? s.toLowerCase() : s.toUpperCase();
}
if (curr.toLowerCase() === curr) {
count--
} else {
count++
}
return process();
}
return process();
};
console.log(stringCase('Aa') === 'aa');
console.log(stringCase('AaA') === 'AAA');
console.log(stringCase('aa') === 'aa');
console.log(stringCase('AA') === 'AA');
So my answer is similar to JBallin's, except that I used a frequency counter to keep track of the upper and lower case counts. This reduces the amount of moving parts for the helper function.
function convertString(str) {
// object to keep track frequency of upper/lower case letters
const frequencyCounter = {
upper: 0,
lower: 0
}
let currentIndex = 0
// helper function to increment frequencies of letters
function checkCase() {
const currentChar = str[currentIndex]
// if current index is outside length of string and returns undefined
// break recursive loop by returning
if (!currentChar) return
if (currentChar.toUpperCase() === currentChar) {
frequencyCounter.upper +=1;
} else {
frequencyCounter.lower +=1;
}
// increment current index and recursively call helper function
currentIndex++
return checkCase()
}
// start helper function
checkCase()
// check for which case to convert the string to and return that string
if (frequencyCounter.upper > frequencyCounter.lower) {
return str.toUpperCase();
} else {
return str.toLowerCase();
}
}
Strings are intense if they end in three or more more ! marks. However, having ! marks anywhere but the end makes for a non-intense string.
The issue I'm having is when there is an ! in the middle of a string. The result should be false but it's still resulting as true.
My code:
function intenseString (str) {
if (str.slice(-3) !== "!!!") {
return false;
}
else if(str.slice(str.indexOf("!"))){
return false;
}
else if(str.slice(-3) === "!!!"){
return true
}
}
Use indexOf instead of slicing the string:
const strings = ['abc!!!', 'abc!!de', 'abc!']
const intenseString = (str, chars) => str.indexOf(chars) >= 0
console.log(strings.map(x => intenseString(x, '!!!')))
Here's your code, with some formatting tweaks for readability:
function intenseString(str) {
if (str.slice(-3) !== '!!!') {
return false;
}
if (str.slice(str.indexOf('!'))) {
return false;
}
if (str.slice(-3) === '!!!') {
return true;
}
}
Can you give some example inputs that return true? I don't think this will ever return true because the second if statement says "return false if there are any !'s in the string, which is implied by passing the first if.
I believe you meant to add + 1: if (str.slice(str.indexOf('!') + 1)) return false which says "return false if there are any !'s that are not the last character in the string", which still won't work: The first if statement will return false if the string doesn't end with !!! meaning that the smallest string that would get to the second if statement is !!! and there will always be characters after the first !.
Another potential attempt would be to only check the string before the last three characters if (str.slice(0, -3).slice(str.indexOf('!') + 1)) return false, which almost works except for strings containing more than 4 ! at the very end... (such as !!!!!).
I don't see a simple way (without regex to check that the remaining characters are only !) to make the second if statement work without looping over the string.
Note that your final if is unnecessary. Your first two are checking for failure and if they pass through them, it must be an intenseString. Also the string must end in !!! if it got past the first if.
Here's a potential solution which goes through every character in the string, except for the last 4, and returns false if there is ever an ! followed by something that is not an !.
function intenseString(str) {
if (!str.endsWith('!!!')) {
return false;
}
for (let i = 0; i < str.length - 4; i++) {
if (str.charAt(i) === '!' && str.charAt(i + 1) !== ('!')) {
return false;
}
}
return true;
}
Try one of these ways:
function intenseString(str) { return str.slice(-3) === '!!!'; }
function intenseString(str) { return str.endsWith('!!!'); }
function intenseString(str) { return /!{3}$/.test(str); }
The function compress() would accept a sentence and return a string with all the blanks and punctuation removed. This function must call isWhiteSpace() and isPunct().
I've already done the functions to call, but I don't know what's missing from my js code to make it call the functions.
function compress(sent) {
var punc = "; : . , ? ! - '' "" () {}";
var space = " ";
if (punc.test(param)) {
return true
} else {
return false
}
if (space.test(param)) {
return true
} else {
return false
}
isWhiteSpace(x);
isPunct(x);
}
This function must call isWhiteSpace() and isPunct().
So you already have two functions which I assume return true when the passed character is either whitespace or a punctuation mark. Then you need not and should not duplicate this functionality by implementing a duplicate regex based text for whitespace and punctuation in your code. Keep it DRY - don't repeat yourself.
A compress function based on these two functions would look as follows:
function isWhiteSpace(char) {
return " \t\n".includes(char);
}
function isPunct(char) {
return ";:.,?!-'\"(){}".includes(char);
}
function compress(string) {
return string
.split("")
.filter(char => !isWhiteSpace(char) && !isPunct(char))
.join("");
}
console.log(compress("Hi! How are you?"));
I agree that a regex test would probably the to-go choice in a real world scenario:
function compress(string) {
return string.match(/\w/g).join("");
}
However, you specifically asked for a solution which calls isWhiteSpace and isPunct.
You can leverage String.indexOf to design the isPunct function.
function isPunct(x) {
// list of punctuation from the original question above
var punc = ";:.,?!-'\"(){}";
// if `x` is not found in `punc` this `x` is not punctuation
if(punc.indexOf(x) === -1) {
return false;
} else {
return true;
}
}
Solving isWhiteSpace is easier.
function isWhiteSpace(x) {
if(x === ' ') {
return true;
} else {
return false;
}
}
You can put it all together with a loop that checks every character in a string using String.charAt:
function compress(sent) {
// a temp string
var compressed = '';
// check every character in the `sent` string
for(var i = 0; i < sent.length; i++) {
var letter = sent.charAt(i);
// add non punctuation and whitespace characters to `compressed` string
if(isPunct(letter) === false && isWhiteSpace(letter) === false) {
compressed += letter;
}
}
// return the temp string which has no punctuation or whitespace
return compressed;
}
If you return something in a function, execution will stop.
From what I can see, your function doesn't need to return anything... So you should just do this
function compress(sent) {
var punc = ";:.,?!-'\"(){} ";
var array = punc.split("");
for (x = 0; x < array.length; x++) {
sent = sent.replace(array[x], "");
}
isWhiteSpace(x);
isPunct(x);
return sent;
}
I've been on this problem for several hours now and have done all I can to the best of my current newbie javaScript ability to solve this challenge but I just can't figure out exactly what's wrong. I keep getting "UNEXPECTED TOKEN ILLEGAL on here: http://jsfiddle.net/6n8apjze/14/
and "TypeError: Cannot read property 'length' of null": http://goo.gl/LIz89F
I think the problem is the howManyRepeat variable. I don't understand why I'm getting it can't read the length of null when clearly word is a word from str...
I got the idea for:
word.toLowerCase().split("").sort().join("").match(/([.])\1+/g).length
...here: Get duplicate characters count in a string
The Challenge:
Using the JavaScript language, have the function LetterCountI(str) take the str
parameter being passed and return the first word with the greatest number of
repeated letters. For example: "Today, is the greatest day ever!" should return
greatest because it has 2 e's (and 2 t's) and it comes before ever which also
has 2 e's. If there are no words with repeating letters return -1. Words will
be separated by spaces.
function LetterCountI(str){
var wordsAndAmount={};
var mostRepeatLetters="-1";
var words=str.split(" ");
words.forEach(function(word){
// returns value of how many repeated letters in word.
var howManyRepeat=word.toLowerCase().split("").sort().join("").match(/([.])\1+/g).length;
// if there are repeats(at least one value).
if(howManyRepeat !== null || howManyRepeat !== 0){
wordsAndAmount[word] = howManyRepeat;
}else{
// if no words have repeats will return -1 after for in loop.
wordsAndAmount[word] = -1;
}
});
// word is the key, wordsAndAmount[word] is the value of word.
for(var word in wordsAndAmount){
// if two words have same # of repeats pick the one before it.
if(wordsAndAmount[word]===mostRepeatLetters){
mostRepeatLetters=mostRepeatLetters;
}else if(wordsAndAmount[word]<mostRepeatLetters){
mostRepeatLetters=mostRepeatLetters;
}else if(wordsAndAmount[word]>mostRepeatLetters){
mostRepeatLetters=word;
}
}
return mostRepeatLetters;
}
// TESTS
console.log("-----");
console.log(LetterCountI("Today, is the greatest day ever!"));
console.log(LetterCountI("Hello apple pie"));
console.log(LetterCountI("No words"));
Any guidance is much appreciated. Thank you!! ^____^
Here is the working code snippet:
/*
Using the JavaScript language, have the function LetterCountI(str) take the str
parameter being passed and return the first word with the greatest number of
repeated letters. For example: "Today, is the greatest day ever!" should return
greatest because it has 2 e's (and 2 t's) and it comes before ever which also
has 2 e's. If there are no words with repeating letters return -1. Words will
be separated by spaces.
console.log(LetterCountI("Today, is the greatest day ever!") === "greatest");
console.log(LetterCountI("Hello apple pie") === "Hello");
console.log(LetterCountI("No words") === -1);
Tips:
This is an interesting problem. What we can do is turn the string to lower case using String.toLowerCase, and then split on "", so we get an array of characters.
We will then sort it with Array.sort. After it has been sorted, we will join it using Array.join. We can then make use of the regex /(.)\1+/g which essentially means match a letter and subsequent letters if it's the same.
When we use String.match with the stated regex, we will get an Array, whose length is the answer. Also used some try...catch to return 0 in case match returns null and results in TypeError.
/(.)\1+/g with the match method will return a value of letters that appear one after the other. Without sort(), this wouldn't work.
*/
function LetterCountI(str){
var wordsAndAmount={};
var mostRepeatLetters="";
var words=str.split(" ");
words.forEach(function(word){
var howManyRepeat=word.toLowerCase().split("").sort().join("").match(/(.)\1+/g);
if(howManyRepeat !== null && howManyRepeat !== 0){ // if there are repeats(at least one value)..
wordsAndAmount[word] = howManyRepeat;
} else{
wordsAndAmount[word] = -1; // if no words have repeats will return -1 after for in loop.
}
});
// console.log(wordsAndAmount);
for(var word in wordsAndAmount){ // word is the key, wordsAndAmount[word] is the value of word.
// console.log("Key = " + word);
// console.log("val = " + wordsAndAmount[word]);
if(wordsAndAmount[word].length>mostRepeatLetters.length){ //if two words have same # of repeats pick the one before it.
mostRepeatLetters=word;
}
}
return mostRepeatLetters ? mostRepeatLetters : -1;
}
// TESTS
console.log("-----");
console.log(LetterCountI("Today, is the greatest day ever!"));
console.log(LetterCountI("Hello apple pie"));
console.log(LetterCountI("No words"));
/*
split into words
var wordsAndAmount={};
var mostRepeatLetters=0;
loop through words
Check if words has repeated letters, if so
Push amount into object
Like wordsAndAmount[word[i]]= a number
If no repeated letters...no else.
Loop through objects
Compare new words amount of repeated letters with mostRepeatLetters replacing whoever has more.
In the end return the result of the word having most repeated letters
If all words have no repeated letters return -1, ie.
*/
The changes made:
[.] turned into . as [.] matches a literal period symbol, not any character but a newline
added closing */ at the end of the code (the last comment block was not closed resulting in UNEXPECTED TOKEN ILLEGAL)
if(howManyRepeat !== null || howManyRepeat !== 0) should be replaced with if(howManyRepeat !== null && howManyRepeat !== 0) since otherwise the null was testing for equality with 0 and led to the TypeError: Cannot read property 'length' of null" issue. Note that .match(/(.)\1+/g).length cannot be used since the result of matching can be null, and this will also cause the TypeError to appear.
The algorithm for getting the first entry with the greatest number of repetitions was wrong since the first if block allowed subsequent entry to be output as a correct result (not the first, but the last entry with the same repetitions was output actually)
-1 can be returned if mostRepeatLetters is empty.
Hope you dont mind if I rewrite this code. My code may not be that efficient.
Here is a snippet
function findGreatest() {
// ipField is input field
var getString = document.getElementById('ipField').value.toLowerCase();
var finalArray = [];
var strArray = [];
var tempArray = [];
strArray = (getString.split(" "));
// Take only those words which has repeated letter
for (var i = 0, j = strArray.length; i < j; i++) {
if ((/([a-zA-Z]).*?\1/).test(strArray[i])) {
tempArray.push(strArray[i]);
}
}
if (tempArray.length == 0) { // If no word with repeated Character
console.log('No such Word');
return -1;
} else { // If array has words with repeated character
for (var x = 0, y = tempArray.length; x < y; x++) {
var m = findRepWord(tempArray[x]); // Find number of repeated character in it
finalArray.push({
name: tempArray[x],
repeat: m
})
}
// Sort this array to get word with largest repeated chars
finalArray.sort(function(z, a) {
return a.repeat - z.repeat
})
document.getElementById('repWord').textContent=finalArray[0].name;
}
}
// Function to find the word which as highest repeated character(s)
function findRepWord(str) {
try {
return str.match(/(.)\1+/g).length;
} catch (e) {
return 0;
} // if TypeError
}
Here is DEMO
function LetterCountI(str) {
var word_arr = str.split(" ");
var x = word_arr.slice();
for(var i = 0; i < x.length; i ++){
var sum = 0;
for(var y = 0; y < x[i].length; y++){
var amount = x[i].split("").filter(function(a){return a == x[i][y]}).length;
if (amount > 1){
sum += amount
}
}
x[i] = sum;
}
var max = Math.max.apply(Math,x);
if(max == 0)
return -1;
var index = x.indexOf(max);
return(word_arr[index]);
};
Here is another version as well.
You could use new Set in the following manner:
const letterCount = s => {
const res = s.split(' ')
.map(s => [s, (s.length - new Set([...s]).size)])
.reduce((p, c) => (!p.length) ? c
: (c[1] > p[1]) ? c : p, []);
return !res[1] ? -1 : res.slice(0,1).toString()
}
Note: I have not tested this solution (other than the phrases presented here), but the idea is to subtract unique characters from the total characters in each word of the phrase.
My whole goal was to write a loop that would take a string, count the letters and return two responses: one = "this word is symmetric" or two = "this word is not symmetric". However the code I wrote doesn't console anything out. Here's the code:
var arya = function(arraycount){
for (arraycount.length >= 1; arraycount.length <= 100; arraycount++) {
while (arraycount.length%2 === 0) {
console.log("This is a symmetric word and its length is " + " " arraycount.length " units.");
arraycount.length%2 != 0
console.log("Not a symmetric word");
}
}
}
arya("Michael");
There are many ways to accomplish your goal, but here are a few. The first is a somewhat naïve approach using a for loop, and the second uses recursion. The third asks whether the string equals the reverse of the string.
iterative (for loop) function
var isPalindromeIteratively = function(string) {
if (string.length <= 1) {
return true;
}
for (var i = 0; i <= Math.floor(string.length / 2); i++) {
if (string[i] !== string[string.length - 1 - i]) {
return false;
}
}
return true;
};
This function begins by asking whether your input string is a single character or empty string, in which case the string would be a trivial palindrome. Then, the for loop is set up: starting from 0 (the first character of the string) and going to the middle character, the loop asks whether a given character is identical to its partner on the other end of the string. If the parter character is not identical, the function returns false. If the for loop finishes, that means every character has an identical partner, so the function returns true.
recursive function
var isPalindromeRecursively = function(string) {
if (string.length <= 1) {
console.log('<= 1');
return true;
}
var firstChar = string[0];
var lastChar = string[string.length - 1];
var substring = string.substring(1, string.length - 1);
console.log('first character: ' + firstChar);
console.log('last character: ' + lastChar);
console.log('substring: ' + substring);
return (firstChar === lastChar) ? isPalindromeRecursively(substring) : false;
};
This function begins the same way as the first, by getting the trivial case out of the way. Then, it tests whether the first character of the string is equal to the last character. Using the ternary operator, the function, returns false if that test fails. If the test is true, the function calls itself again on a substring, and everything starts all over again. This substring is the original string without the first and last characters.
'reflecting' the string
var reflectivePalindrome = function(string) {
return string === string.split('').reverse().join('');
};
This one just reverses the string and sees if it equals the input string. It relies on the reverse() method of Array, and although it's the most expressive and compact way of doing it, it's probably not the most efficient.
usage
These will return true or false, telling you whether string is a palindrome. I assumed that is what you mean when you say "symmetric." I included some debugging statements so you can trace this recursive function as it works.
The Mozilla Developer Network offers a comprehensive guide of the JavaScript language. Also, here are links to the way for loops and while loops work in JS.