Why does javascript 'if statements' return undefined? - javascript

This is more of a 'why does this happen' question than a trouble shooting question.
I am trying to create a function that tests whether a word is a palindrome. I thought initially that I can use an if statement it compare the word and when it is reversed. However the if statement gave me undefined. When I compared it regularly (word === reverseWord), it came back with a truth or false. My question is what is going on in the if statement for it do wonk out? Thanks!
const palindromes = function(word) {
*//Turn word into an array*
var arrWord = Array.from(word);
*//Reverse the array*
var reversedWord = arrWord.reverse();
*//This is true*
return arrWord === reversedWord;
//But this is undefined?
if (arrWord === reversedWord) {true} else {false};
}

Its returning undefined because you are not using return keyword anywhere.
Another mistake you are making is that you are comparing two arrays which reference same object in memory it arrWord === reversedWord will always return true.
Convert word to array using split('') and reverse() it. And then join(''). And finally compare reversed string and word
const palindromes = function(word) {
var reversedWord = word.split('').reverse().join('');
console.log(word === reversedWord);
if (word === reversedWord) {
return true
} else {
return false
};
}
console.log(palindromes("aba"))
console.log(palindromes("ba"))
The code can be reduced to a single line.
const palindromes = (word) => word.split('').reverse().join('') === word
console.log(palindromes("aba"))
console.log(palindromes("ba"))

Related

Javascript recursive example

I recently had an interview where you had to recursively go over a string, and if it contained an AB || BA || CD || DC, it had to be deleted from the array. You would recursively go over this as deleting the CD from ACDBB would give you an AB which you would then have to delete to return a B as a string.
This is what I have, and when I test it out, I see it comes up with the right answer deep in the loops, but it never populates back to the top.
What am I missing?
const LETTERS = [/AB/g, /BA/g, /CD/g, /DC/g];
const stringGame = (string) => {
let newString = '';
if(string.length <= 1) return string;
LETTERS.forEach(regExToCheck => {
if(string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
stringGame(newString);
})
return newString
}
// Expect answer: CAACC
console.log(stringGame('ABDCABCABAAABCCCD'))
Move the recursion,
return the recursion, and
Add an essential condition to end recursion:
const LETTERS = [/AB/g, /BA/g, /CD/g, /DC/g];
const stringGame = (string) => {
let newString = '';
if (string.length <= 1) return string;
LETTERS.forEach(regExToCheck => {
if (string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
})
// Moved, returned, and ended recursion
return ('' === newString) ? string : stringGame(newString);
}
// Expect answer: CAACC
console.log(stringGame('ABDCABCABAAABCCCD'))
When recursing the function, i.e., when executing the function within itself, the return'ed value of that execution needs to be used, typically return'ed:
return stringGame(newString);
...in order for it to bubble back up the levels of recursion.
Also, since the replace() function is using a regex global replacement flag (g) all, for example, AB's, are being replaced in a single execution, so there's no need to recurse within the forEach() loop:
LETTERS.forEach(regExToCheck => {
if(string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
stringGame(newString); // <-- double oops
})
Rather, recurse after the loop, AND return the value of the execution:
LETTERS.forEach(regExToCheck => {
if(string.match(regExToCheck)) {
newString = string.replace(regExToCheck, '')
}
})
return stringGame(newString);
One more principle of recursive functions is providing exit strategies—define what conditions to end the recursion, bubble back up, and return a final result.
The only exit condition provided is:
if (string.length <= 1) return string;
Surely if there's only one character left no match will be found—a proper time to end recursion. However, and more importantly, there also needs to be a path to exit when there are more than one characters but no matches can be found.
There are multiple ways to determine this but in the case provided in the question the most expedient is when after looping through the regex/replace routine no changes were made to the string. Since with every recursion the stringGame() function instantiates an empty string (newString) if, after looping through the regex/replace routine, newString is still an empty string, this indicates there were no matches found and it's time to end recursion:
if ( '' === newString ) {
return string;
}
else {
return stringGame(newString);
}
...otherwise, recurse.
The stringGame function doesn't have side effects, so the line in the loop here:
stringGame(newString);
doesn't do anything - you need to communicate the result of the recursive call back to the outer level.
A nicer way to approach this would be to combine the LETTERS into a single regular expression, and then to replace all matches with the empty string until the replacement produces no changes.
const LETTERS = [/AB/g, /BA/g, /CD/g, /DC/g];
const pattern = new RegExp(
LETTERS
.map(re => {
const str = String(re);
return str.slice(1, str.length - 2);
})
.join('|'),
);
const stringGame = (string) => {
const newString = string.replace(pattern, '');
return newString === string
? string
: stringGame(newString);
}
// Expect answer: CAACC
console.log(stringGame('ABDCABCABAAABCCCD'))
(If you want to use the global flag in the constructed pattern, you'll have to account for the .lastIndex)

My code is not comparing two different strings correctly

I am attempting to solve this codewars problem:
Complete the function scramble(str1, str2) that returns true if a
portion of str1 characters can be rearranged to match str2, otherwise
returns false.
examples:
scramble('rkqodlw', 'world') ==> True
scramble('cedewaraaossoqqyt', 'codewars') ==> True
scramble('katas', 'steak') ==> False
This is my attempt:
function scramble(str1, str2) {
let obj1 = {};
let obj2 = {};
for (el of str1) {
obj1[el] = (obj1[el] || 0) + 1;
}
for (el of str2) {
obj2[el] = (obj2[el] || 0) + 1;
}
for (el in obj2) {
if (!(el in obj1)) return false;
}
return true;
}
I am converting the strings to objects, and then comparing the keys to each other. My code passes about 90% of all the tests on Codewars, but then it does not pass the other 10% and they do not show what the test inputs are unfortunately.
My hunch is that there are a few edge cases that this code is not catching. Any help would be appreciated.
You will need to handle the case when str2 has more instances of a letter than str1.
For example:
scramble("a", "aa")
Which should evaluate to false (not enough 'a's in "a" to form "aa").
You need to handle the amount of characters. Now you just checking whether the character in str2 also exists in str1.
So instead of:
for (el in obj2) {
if (!(el in obj1)) return false;
}
Try:
for (let [key, value] of Object.entries(obj2)) {
if (obj1[key] === undefined || obj1[key] < value) return false;
}
Which means that if obj1[key] doesn't exist or it has less occurrences than obj2[key] it will return false.
this code maybe helps
function scramble(str1, str2) {
let occurences = str1.split("").reduce((arr, cur) => { arr[cur] ? arr[cur]++ : arr[cur] = 1; return arr; }, {});
console.log(occurences);
return str2.split("").every((character) => --occurences[character] >= 0);
}
console.log(scramble("awpoirwled", "world"));
The algorithm I would use is to take each letter of the match string, and if its got an equivalent in the searched string, remove it from there and continue, otherwise abort if there is no match.
If you reach the end of the match string without aborting then you have a match

Word Count function is picking up "" as a viable word?

I've written a basic function to count word frequency within a string. First, I split the string into an array, then I iterate through the array of words via for loop. That said, if initial string is empty (""), my function will regard "" as a word and thus the result is {"": 1} rather than an empty object. I initially thought that the empty string wouldn't register at all, due to the for-loop.
I've fixed this issue through an if conditional at the start of the function, but I was wondering if there was a better way to go about it.
function countWords(str) {
if (str === "") {
return {};
}
var counts = {};
var wordArray = str.split(" ");
for (i=0;i < wordArray.length; i++) {
word = wordArray[i];
if (!counts[word]) {
counts[word] = 1;
} else {
counts[word] += 1;
}
}
return counts;
}
My Ruby code equivalent did not require anything to not pick up the empty string as a viable word target, so this bothers me somewhat.
Edit: Thank you to all the responses. I appreciate all the help.
Depends on the situation, but if in your case only strings are passed as parameter that check is good enough.
If there's possibility of undefined, null or other falsy value being passed as a parameter as well, you could do this check instead:
if (!str) {
return {};
}
If you want a check even stricter you could do:
if (!str || typeof str !== 'string') {
return {};
}
In this case you are checking falsy values(as we saw above empty string is a falsy value) and making sure you are getting as parameter a string.
This is expected behavior. .split() does not guarantee non-empty strings in the results. Unless you filter out empty strings explicitly, all empty strings that .split() returns will be counted.
Since .split does not coalesce sequential split strings, you may have a lot of empty strings after split: try "a b c".split(" ").

How to check if a string only contains chars from an array?

I try to check if a word (wordToCheck) only consists of letters from an array (letters) and also contains every letter in the array only as often (or rather not more times than they are in the array) as it actually is inside of the array.
Here are examples of what the desired function should return:
checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]) === true
checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]) === false
How can I make this code work?
function checkIfWordContainsLetters(wordToCheck, letters) {
var lettersToString = letters.toString();
var lettersTrimmed = lettersToString.replace(/,/gi, "?");
var regEx = new RegExp(lettersTrimmed, "gi");
if (wordToCheck.match(regEx)!== null) {
return true;
}
else return false;
}
You could use this ES6 function:
function checkIfWordContainsLetters(wordToCheck, letters){
return !letters.reduce((a, b) => a.replace(b,''), wordToCheck.toLowerCase()).length;
}
console.log(checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]));
console.log(checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]));
The idea is to go through each letter in the letters array, and remove one (not more!) occurrence of it in the given wordToCheck argument (well, not exactly in it, but taking a copy that lacks that one character). If after making these removals there are still characters left over, the return value is false -- true otherwise.
Of course, if you use Internet Explorer, you won't have the necessary ES6 support. This is the ES5-compatible code:
function checkIfWordContainsLetters(wordToCheck, letters){
return !letters.reduce(function (a, b) {
return a.replace(b, '');
}, wordToCheck.toLowerCase()).length;
}
console.log(checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]));
console.log(checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]));
As long as it is not the best solution for long strings for which using some clever regex is definitely better, it works for short ones without whitespaces.
function checkIfWordContainsLetters(word, letters){
word = word.toLowerCase().split('');
for(var i = 0; i < letters.length; i++) {
var index = word.indexOf( letters[i].toLowerCase() );
if( index !== -1 ) {
// if word contains that letter, remove it
word.splice( index , 1 );
// if words length is 0, return true
if( !word.length ) return true;
}
}
return false;
}
checkIfWordContainsLetters("google", ["a","o","o","g","g","l","e","x"]); // returns true
checkIfWordContainsLetters("google", ["a","o","g","g","l","e","x"]); // returns false

Checking if one element of an array's index is equal to the second element

I have this little script that will check if one element of an array (arr[0]) is equal to the second element of the array (arr[1]). However when it checks the following array I would expect it to return false, yet it returns true. so my questions are, why does this return true, and how can I fix it to return false like expected?
function mutation(arr) {
var elem0 = arr[0].toLowerCase();
var elem1 = arr[1].toLowerCase();
for(var x=0; x < elem1.length; x++){
check = elem0.indexOf(elem1[x]);
if(check === -1){
return false;
}
return true;
}
}
mutation(["hello", "hey"]); //returns true
you place the return true to soon
you need to place it after the for statement like so
function mutation(arr) {
var elem0 = arr[0].toLowerCase();
var elem1 = arr[1].toLowerCase();
for(var x=0; x < elem1.length; x++){
check = elem0.indexOf(elem1[x]);
if(check === -1){
return false;
}
}
return true;
}
mutation(["hello", "hey"]); //returns false
You're looping over a characters in a string (see what elem1 actually is), and therefore you get true because the first character of hey, which is h, is indeed found within the string hello.
If you want to wait for it to finish iterating over the whole string, use a boolean flag, and then return the value of that flag when the iterations are over.
However, seems you just want to compare the two elements:
return elem0 === elem1;
I have this little script that will check if one element of an array
(arr[0]) is equal to the second element of the array (arr[1])
It returns true since e is in both the elements hello and hey
Your code is essentially iterating over all the characters in the string.
You need to simply check
function mutation(arr) {
return arr[0].toLowerCase() == arr[1].toLowerCase();
}
The expression of this question has some logical flaws or at least some lacking points. Such as the given condition means that all the items in the array must be equal. If this is the case then just one tiny piece of instruction is sufficient
myArray.every(e => e == myArray[0])
var a = [1,1,1,1,1,1,1,1],
b = ["hello", "hey"];
document.write("<pre> a array :" + a.every(e => e == a[0]) + "</pre>");
document.write("<pre> b array :" + b.every(e => e == b[0]) + "</pre>");

Categories