I am learning JS on freecodecamp and am stuck on a problem. I am creating a palindrome checker. My code almost works. However, there is only one word that is not passing the test and that is almostomla. It is not a palindrome yet my code returns true. I have tried several things. Even rewrote the code and used the while loop but nothing seems to help. There is a solution at the freeCodecamp web but I have written the code in a different way and am unable to figure my mistake out.
Here is my code.
let reversedStr = [];
function palindrome(str) {
let d = str.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
for (let i = d.length - 1; i >= 0; i--) {
reversedStr.push(d[i]);
}
for (let j = 0; j < str.length; j++) {
if (reversedStr[j] == d[j]) {
return true;
} else {
return false;
}
}
}
console.log(palindrome("almostomla"));
This line if (reversedStr[j] == d[j]) {return true; returns as soon as the character matches at both the index. It does not check rest of the characters.
In fact you can just return as soon as the character in both index does not match.
Also note the reversedStr has to be inside the function. Else it will contain previous values
function palindrome(str) {
let reversedStr = [];
let d = str.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
for (let i = d.length - 1; i >= 0; i--) {
reversedStr.push(d[i]);
}
for (let j = 0; j < str.length; j++) {
if (reversedStr[j] !== d[j]) {
return false;
}
}
return true
}
console.log(palindrome("almostomla"));
console.log(palindrome("1221"));
Your code returns "true" or "false" after first match - it checks that 'a' is equal to 'a' and returns true.
You can return "false" from inside of cycle only if you found duplicated letter else return true after the cycle ends.
Related
I'm trying to solve a problem on Codewars which involves seeing if one string includes all the letters in a second string. I think I've found a decent solution, but my code times out (12000ms) and I can't figure out why. Could anyone shed some light on this issue?
function scramble(str1, str2) {
let i;
let j;
let x = str2.split();
for (i = 0; i < str1.length; i++) {
for (j = 0; j < str2.length; j++) {
if (str1[i] == str2[j]) {
x.splice(j, 1);
j--;
}
}
}
if (x.length == 0) {
return true;
} else {
return false;
}
}
If your strings have sizes N and M then your algorithm is O(N*M). You can get O(NlogN + MlogM) by sorting both strings and then do a simple comparison. But you can do even better and get O(N+M) by counting the letters in one string and then see if they are present in the other. E.g. something like this:
function scramble(str1, str2) {
let count = {}
for (const c of str1) {
if (!count[c])
count[c] = 1
else
count[c]++
}
for (const c of str2) {
if (!(c in count))
return false
count[c]--
}
for (let k in count) {
if (count.hasOwnProperty(k) && count[k] !== 0)
return false
}
return true
}
You created an infinite loop by both incrementing and decrementing j. The value of j gets stuck whenever str1[i] == str2[j]
Reducing your code snippet to the simplest form would look something like this:
for (j = 0; j < 10; j++) {
j--;
console.log(j) // always -1
}
You're adjusting x but then referring to str2 as if it has been changed. Because you never adjust str2, you're always comparing the same two letters, so you get stuck in a loop. That's one problem. Then, your question's wording suggests that we're checking if every letter in str2 is in str1, but you're going through every letter in str1 and checking it against str2. str1 should be the inner loop.
function scramble(str1, str2) {
var x = str2.split("");
for (var i = 0; i < x.length; i++) {
for (var j = 0; j < str1.length; j++) {
if (str1[j] == x[i]) {
x.splice(i--, 1);
}
}
}
return x.length === 0;
}
console.log(scramble("dirty rooms", "dormitory"));
console.log(scramble("cat", "dog"));
Because x.length === 0 is already a boolean value, you can just return that. No need for the if statements there. The triple equals checks the variable's type and value, and double equals only checks value. I tend to always use triple when I'm checking against 0 and 1 because you don't want unintended consequences like this:
console.log(false == 0, true == 1);
console.log(false === 0, true === 1);
Also, because i-- means "i = i - 1 after execution", you can put that directly in your call to splice, and it won't execute until after splice is finished. --i, on the other hand, would be evaluated before execution.
This is all great, but using indexOf is a simpler solution:
function scramble(str1, str2) {
for (let i = 0; i < str2.length; i++) {
if (str1.indexOf(str2[i]) == -1) return false;
}
return true;
}
console.log(scramble("forty five", "over fifty"));
console.log(scramble("cat", "dog"));
I have a function that takes an input of a string and a single char that will count how many times that char appears in that string.
function count(str, letter) {
var num = 0;
for (var i = 0; i < str.length; i++)
if (str.charAt(i) == letter)
num += 1;
return num;
}
console.log(count("BBC", "B"));
//output 2
It works fine like this, but this took me some time to figure out. Its second hand nature for me to always put brackets on a for loop but when i do that, the function doesn't work as i anticipated it would, like so:
function count(str, letter) {
var num = 0;
for (var i = 0; i < str.length; i++) {
if (str.charAt(i) == letter)
num += 1;
return num;
}
}
console.log(count("BBC", "B"));
//outputs 1
Why are the brackets causing it to act this way?
Why are the brackets causing it to act this way?
Because you have the return statement inside of the for loop block. At the end of the block, the function returns.
function count(str, letter) {
var num = 0;
for (var i = 0; i < str.length; i++) { // block start
if (str.charAt(i) == letter)
num += 1;
return num; // exit function in first loop
} // block end
}
It's not the braces (brackets are []), it's the placement of the return statement. The return statement is in the first iteration of the loop (i = 0). If you add an extra set of braces (as seen below), it becomes more obvious.
function count(str, letter) {
var num = 0;
for (var i = 0; i < str.length; i++) {
if (str.charAt(i) == letter) {
num += 1;
}
return num; // <-- This return exits the function
}
}
console.log(count("BBC", "B"));
//outputs 1
In the First one return statement was outside for loop, but in the second one return statement is inside the for loop. That made the difference.
Try the below code.
function count(str, letter) {
var num = 0;
for (var i = 0; i < str.length; i++) {
if (str.charAt(i) == letter)
num += 1;
}
return num;
}
console.log(count("BBC", "B"));
your loop gets terminated after first iteration.
So if you try to get the occurrence of "B" in "XBBBB...B" it will return 0.
Try to debug your code and place brackets at right position.
Learn to debug your js code using browser.
Actually I found an answer a few minutes ago.
But I found something strange.
This is my answer for 'Missing letters' in freeCodeCamp challenges.
function fearNotLetter(str) {
var string;
for (i=0;i<str.length;i++) {
if(str.charCodeAt(i)+1 < str.charCodeAt(i+1)){
string = String.fromCharCode(str.charCodeAt(i)+1);
}
}
return string;
}
When I change < operator in if statement into != (not same), it doesn't work!
For me, it seems that != works exactly same as < operator does.
(Because 'not same' can mean something is bigger than the other.)
What is the difference between < and != in the code above?
Your code has a small defect that works when you use < but not !=.
If you see str.charCodeAt(i+1); this code is checking one spot past the end of the string on the last iteration and will return a NaN result.
If I provide the string "abce" it will check if f is < NaN. I believe NaN can't be compared to f's value so it doesn't go into the if statement. So it will keep the missing letter d that was found in the previous iterations which is stored in your string variable.
However, if you provide the !=, then with the same scenario it knows f != NaN and goes into the if statement. This then overwrite the actual missing letter and fails your FCC test case because it is replacing the missing d with f in your string variable.
To fix your code, simply change the for loop to end one iteration before the length of the string.
for (i = 0; i != str.length-1; i++) {
}
This is my method without using .charCodeAt() function :)
function fearNotLetter(str) {
var ind;
var final = [];
var alf =['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'];
str = str.split('');
ind = alf.splice(alf.indexOf(str[0]),alf.indexOf(str[str.length-1]));
for(var i=0;i<ind.length;i++){
if(str.indexOf(ind[i]) == -1){
final.push(ind[i]);
}
}
if(final.length != 0){
return final.join('');
}
return;
}
fearNotLetter("bcef");
My solution:
function fearNoLetter(str){
var j= str.charCodeAt(0);
for(var i=str.charCodeAt(0); i<str.charCodeAt(str.length-1); i++){
j = str.charCodeAt(i - str.charCodeAt(0));
if (i != j){
return String.fromCharCode(i);
}
}
}
My solution:
function fearNotLetter(str) {
let y = 0;
for (let i = str.charCodeAt(0); i < str.charCodeAt(str.length - 1); i++) {
if (str.charCodeAt(y) != i) {
return String.fromCharCode(i);
}
y++;
}
return;
}
console.log(fearNotLetter("ace"));
function fearNotLetter(str) {
let alpha = "abcdefghijklmnopqrstuvwxyz";
let alphabet = []
for(let j = 0; j< alpha.length; j++){
alphabet.push(alpha[j])
}
if (alphabet.length == str.length){
let result = undefined;
return result
}else{
const start =alphabet.indexOf(str[0])
let end = (str.length)-1
const stop = alphabet.indexOf(str[end])
const finish = alphabet.slice(start,stop)
let result = finish.filter(item => !finish.includes(item) || !str.includes(item))
result = String(result)
return result
}
return result
}
console.log(fearNotLetter("abcdefghijklmnopqrstuvwxyz"));
I'm in the process of learning functional programming, and completely getting rid of for loops has been a challenge sometimes, because they provide so much control and freedom. Below is an example of checking if a string is an isogram or not (no letters should be repeated). With nested for loops, it became an easy solution. Is there a way to do this the functional way with any high order functions or anything else? Any suggestion would be a huge help.
Code:
function isIsogram(string) {
let array = string.split('');
let condition = true;
for (let i = 0; i < string.length; i++) { //first loop picks character
for (j = i + 1; j < string.length; j++) { //second loop compares it rest
if (array[i].toLowerCase() == array[j].toLowerCase())
condition = false; //if repeat, the condition false
}
return condition;
}
}
You can use every or some together with a suitable string function:
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
return string.split('').every(function(character, index) {
return !string.includes(character, index+1);
});
}
Instead of includes you might also have utilised indexOf.
You can sort the String first and then apply every on it. It will stop the iteration as soon as two successive letters are the same:
Here is an improved implementation. Credit goes to #Xotic750:
function isIsogram(x) {
return Array.from(x.toLowerCase()).sort().every((y, i, xs) => i === 0
? true
: y !== xs[i - 1]);
}
console.log( isIsogram("consumptively") );
console.log( isIsogram("javascript") );
The implementation uses Array.prototype.every's second parameter, which represents the index of the current element (of the iteration). Please note that isIsogram solely depends on functions and their arguments.
Another example, like #Bergi but using some ES6 features for comparison.
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
for (let character of Array.from(string).entries()) {
if (string.includes(character[1], character[0] + 1)) {
return false;
}
}
return true;
}
console.log(isIsogram('abc'));
console.log(isIsogram('abca'));
How your ES3 style code could have looked (noting some of the issues pointed out in the comments)
function isIsogram(string) {
string = string.toLowerCase(); // case insensitive
var length = string.length;
for (var i = 0; i < length; i += 1) {
for (var j = i + 1; j < length; j += 1) {
if (string.charAt(i) === string.charAt(j)) {
return false;
}
}
}
return true;
}
console.log(isIsogram('abc'));
console.log(isIsogram('abca'));
I'm writing a function that takes a string as an argument, checks it for a given character (say "B" in this case), and then returns an integer that reflects the number of times that character appeared. I'm aware that this can be done using regex and such, but the tutorial I'm using has so far made no mention of regex. Code time:
function countBs(string) {
var i = 0;
var n = 0;
var position = string.charAt(n);
while (i < string.length) {
if (string.charAt(n) == "B")
n += 1;
i++; //This line causes the following else statement to throw a syntax error. But it's the only way I can think of to have the loop continue iteration *while* checking for equivalence to "B"
else
i++;
return n;
}
}
And then check with console.log(countBs("ABBA"));
Your code is quite broken.
function countBs(string) {
var i = 0;
var n = 0;
// var position = string.charAt(n); // REMOVE--NOT NECESSARY
while (i < string.length) {
if (string.charAt(i) == "B") // i, NOT n
n++; // CONSISTENCY IN ADD-ONE SYNTAX
// i++; // INCREMENT ONCE BELOW
//else
i++;
}
return n; // MUST GO OUTSIDE THE LOOP
}
Correct code would therefore be:
function countBs(string) {
var i = 0;
var n = 0;
while (i < string.length) {
if (string.charAt(i) == "B") n++;
i++;
}
return n;
}
There's nothing particularly wrong with using a while loop, but a for would be more natural:
function countBs(str) {
var n = 0;
for (var i = 0; i < str.length; i++) if (str[i]== "B") n++;
return n;
}
Modern JS
For your reference, in modern JS, you could avoid the loops and variables. First, let's write a separate checking function:
function isB(c) { return c === 'B'; }
Then write
function countBs(str) {
return str . split('') . filter(isB) . length;
}
or, using reduce:
function countBs(str) {
return str.split('').reduce(function(cnt, c) {
return cnt + isB(c);
}, 0);
}
or, although you said you didn't want to use regexps:
function countBs(str) {
return (str.match(/B/g) || []) . length;
}
If you are writing in an ES6 environment, then using array comprehensions
function countBs(str) {
return [for (c of str) if (isB(c)) c] . length;
}
Try wrapping it in curly braces:
if (string.charAt(n) == "B")
{ n += 1;
i++;
}
An else requires a previous if, and no other statements in between. i++ was outside the if.
Here's my answer
function countBs(Str)
{
let char = "B" ;
return String(Str).split(char).length - 1;
}
function countChar(Str, char)
{
return String(Str).split(char).length - 1;
}