Isomorphic Strings Function Always Returns True - javascript

I am attempting the Isomorphic Strings problem on LeetCode and am having issues with my current solution. I'm sure there are plenty of answers on exactly how to complete this problem, but I would really prefer to finish it through my own thought process before learning the best possible way to do it. For reference, here is the problem: https://leetcode.com/problems/isomorphic-strings/?envType=study-plan&id=level-1
This is my code as it is right now:
var isIsomorphic = function(s, t) {
const map = new Map();
const array1 = [...s];
const array2 = [...t];
for (i = 0; i < s.length; i++) {
if ((map.has(array1[i]) === true) && (map.has(array2[i]) === true)) {
if (map.get(array1[i]) !== array2[i]) {
return false;
} else {
continue;
}
} else if (map.has(array1[i]) === false) {
map.set(array1[i], array2[i]);
}
}
return true;
};
It's messy but I can't figure out why it isn't giving me the desired results. Right now, it seems to always return true for any given values, even though I have the initial if statement to return false if it ever comes across previously-mapped values that don't match. Am I missing something obvious? This is my first question on SA, so I apologize if the format is wrong.

The map is set like:
map.set(array1[i], array2[i]);
The key is the character in the first string, and the value is the corresponding character in the second string. So, when iterating over a new character, checking map.has will only make sense if the character being passed is from the first string; doing map.has(array2[i]) === true)) does not test anything useful, because the second string's characters are not keys of the Map.
You need to perform two tests: that the 1st string's character corresponds to the 2nd string's character (which you're doing right), and that the 2nd string's character is not already set to a different 1st string's character (which needs to be fixed). For this second condition, consider having another Map that's the reverse of the first one - the keys are the characters from the 2nd string, and the values are the characters from the 1st string. (You don't have to have another Map - you could also iterate through the .entries of the first, check that, for every entry's value that matches the 2nd character, the entry's key matches the 1st - but that could feel a bit messy.)
Cleaning up your code some, there's also no need to turn the strings into arrays, and === true can be omitted entirely, and the i variable should be declared with let.
You also might want to check if the length of the first string is equal to the length of the second.
var isIsomorphic = function(s1, s2) {
if (s1.length !== s2.length) return false;
const map1to2 = new Map();
const map2to1 = new Map();
for (let i = 0; i < s1.length; i++) {
// Check that s1 here corresponds to s2
if (map1to2.has(s1[i]) && map1to2.get(s1[i]) !== s2[i]) {
return false;
}
// And that s2 here doesn't correspond to any other s1
if (map2to1.has(s2[i]) && map2to1.get(s2[i]) !== s1[i]) {
return false;
}
map1to2.set(s1[i], s2[i]);
map2to1.set(s2[i], s1[i]);
}
return true;
};
console.log(isIsomorphic('aa', 'bb'));
console.log(isIsomorphic('aa', 'ba'));
console.log(isIsomorphic('badc', 'baba'));

Related

Why isn't this constraint in my code causing a specific value to be returned, even though the debugger displays the specific value as a return value?

I am running through some practice problems on CodeWars right now and have stumbled upon an issue that I cannot seem to figure out. The problem is straightforward and simple enough:
My function should take a string input, and return the first character that is not repeated anywhere in the string.
For example, if given the input 'stress', the function should return 't', since the letter t only occurs once in the string, and occurs first in the string.
As an added challenge, upper- and lowercase letters are considered the same character, but the function should return the correct case for the initial letter. For example, the input 'sTreSS' should return 'T'.
If a string contains all repeating characters, it should return an empty string ("").
My code is as follows:
const firstNonRepeatingLetter = (string) => {
const letters = {};
// Object that I am assigning key/value pairs to based on the number of occurrences of each letter
string.split('')
.forEach(letter => {
letter = letter.toLowerCase();
if (letters[letter] !== undefined) letters[letter]++;
if (letters[letter] === undefined) letters[letter] = 1;
});
for (let key in letters) {
let val = letters[key];
// Iterate through object and upon finding the first value of 1, evaluate the casing of the key
if (val === 1) {
string.split('')
.forEach(letter => {
// *line16* If the letter in the string is the same as the corresponding key in uppercase, return letter
if (key.toUpperCase() === letter) return letter;
});
// Otherwise, return the key as is
return key;
};
};
return '';
};
This passes all tests except for the 'sTreSS' test. I have ran through the process with the debugger, and ironically, the *if conditional on line 16 displays a local Return value: 'T' when reaching this point. However, the forEach continues to iterate and I end up with a return value of 't', which is the next return statement in my block.
I am confounded by this and would appreciate any insight into what is causing the issue.
You could instead use Array.find.
if (val === 1) {
const findIt = string.split('').find(letter => key.toUpperCase() === letter);
return findIt || key; // if findIt is undefined (not found), return key
};
As mdn says:
There is no way to stop or break a forEach() loop other than by
throwing an exception. If you need such behavior, the forEach() method
is the wrong tool.
Early termination may be accomplished with:
A simple for loop
A for...of / for...in loops
Array.prototype.every()
Array.prototype.some()
Array.prototype.find()
Array.prototype.findIndex()
Array methods: every(), some(), find(), and findIndex() test the array
elements with a predicate returning a truthy value to determine if
further iteration is required.
As an alternative, you can use findIndex:
const findIndex = string.split('').findIndex(letter => key.toUpperCase() === letter);
return findIndex != -1
? string.split[findIndex ]
|| key;
But find() is neater :)

Javascript object

I came across a problem in an online course:
Write a function called vowelCount which accepts a string and returns an object with the keys as the vowel and the values as the number of times the vowel appears in the string. This function should be case insensitive so a lowercase letter and uppercase letter should count
Examples:
vowelCount('Elie') // {e:2,i:1};
the solution from the instructor came like this:
function vowelCount(str){
var splitArr = str.toLowerCase().split("");
var obj = {};
var vowels = "aeiou";
splitArr.forEach(function(letter){
if(vowels.indexOf(letter) !== -1){
if(obj[letter]){
obj[letter]++;
} else{
obj[letter] = 1;
}
}
});
return obj;
}
I understand the solution until the second "if" statement. I know that the first "if" statement is to check if the "letters" in the input string belongs to the "vowels". Then in the second "if" it is checking if the "letter is in the empty "obj" object created above, but at that line, the "obj" is empty bofore the "letter" is added to it, so what is the point for that "if". Also, why does adding this new "letter" to the object require an increment. I tried the code without increment and the object is still empty.
It's checking if you've ever seen the letter before in the loop. If you've never written to obj[letter], then when you do obj[letter], you get back the value undefined, which is falsy (treated as false by things like an if). if(obj[letter]) is checking for a truthy value (a value that isn't falsy) so that it adds to the number already stored at obj[letter] if it's there (obj[letter]++). But when it sees a falsy value like undefined, it takes the else branch and sets obj[letter] to 1 because the code knows that letter hasn't been seen before.
Just FWIW, while still entirely valid, that's fairly old-style JavaScript code (circa the ES5 standard, 2009). ES2015 added several features you'd use to solve this problem today:
function vowelCount(str){
// Use a Map to remember how many of each ltter you've
// seen. You could use an object as well, but ideally you'd
// create the object with out a prototype to avoid having
// any conflict with inherited properties from `Object.prototype`.
const counts = new Map(); // const counts = Object.create(null);
// The set of vowels
const vowels = new Set("aeiou");
// Loop through the letters
for (const letter of str) {
// If it's not a vowel...
if (!vowels.has(letter)){
// Count it
const currentCount = counts.get(letter) || 0;
counts.set(letter, currentCount + 1);
// Or using an object:
// const currentCount = counts[letter] || 0;
// counts[letter] = currentCount + 1;
}
});
return counts;
}
We can use regular expression to match vowels in a sentence.
Regular expression to match all occurrence of vowel in a string:/[aeiouAEIOU]+?/g
Below is the working code snippet:
//function that takes string as input
//function returns an object containing vowel count without case in account.
function vowelCount(input) {
//to get vowel count using string.match
var arrVowels =input.match(/[aeiouAEIOU]+?/g);
//acc=accumulator, curr=current value
return arrVowels.reduce(function (acc, curr) {
if (typeof acc[curr.toLowerCase()] == 'undefined') {
acc[curr.toLowerCase()] = 1;
}
else {
acc[curr.toLowerCase()] += 1;
}
return acc;
// the blank object below is default value of the acc (accumulator)
}, {});
}

Compare a word against words in an array

Write a function offOne(word, book) which takes
a string called word and an array of strings called book.
It returns an array of all the words in book of the same
length that are one letter different.
Examples:
offOne("cat", ["cat", "fat", "flat", "tar"]) => ["fat", "tar"]
offOne("will", ["wilt", "willow", "wail"]) => ["wilt", "wail"]
My function is currently:
function offOne(word, book) {
var array = [];
var count = 0;
for (var i = 0; i < book.length; i++) {
if (book.length === word.length) {
if (word.indexOf(book[i]) !== -1) {
count += 1;
if (count === (book[i].length - 1)) {
array.push(book[i]);
}
}
}
}
return array;
}
Does anyone know how to solve this? I got stuck for a while here.
The snippets are well addressed with comments. It should help you. Do Check it!
Points to remember for your prep:
Don't declare unnecessary variables. It consumes memory, which is bad.
Don't use unnecessary loops. Check for available language API before using loops. Like, I used filter instead of foreach. These will reduce your work.
Always consider Logical operators.
Make the code dead simple.
Best of luck for your course!
My way of doing it
var word = "cat";
var book = ["car", "far", "mars", "call", "bat"]
function compare(elm, word) {
var i = 0
elm.split('').forEach(c => { //tokenize elm of book into array
if (word.indexOf(c) > -1) //check if charecter in present in the word
i += 1 //if yes, increment
})
return i === word.length - 1 ? true : false //return true if length of i is (length of word - 1),
}
function offOne(word, book) {
return book.filter(elm =>
// check, if the length of both strings are not same and
// both strings are not same and
// compare strings, true will be returned if the condition is satisfied in compare()
elm.length === word.length && elm !== word && compare(elm, word)
)
}
console.log(offOne(word, book))
My advanced way of doing it
If you see, this one doesn't have any variables declared inside the functions.
var word = "cat";
var book = ["car", "far", "mars", "call", "bat"]
function compare(elm, word) {
return elm.split('').filter(c => //tokenize elm of book into array
word.indexOf(c) > -1 //check if charecter in present in the word, if yes, return true
).join('').length === word.length - 1 ? true : false //join and check the length of the array is one less than length of the word, if yes, return true
}
function offOne(word, book) {
return book.filter(elm =>
// check, if the length of both strings are not same and
// both strings are not same and
// compare strings, true will be returned if the condition is satisfied in compare()
elm.length === word.length && elm !== word && compare(elm, word)
)
}
console.log(offOne(word, book))
Convert word to array of letters, make this array unique. For each book array item do the same and calculate the number of different characters between them. If only one difference is found, return the item, repeat for each.

Check if the first letter of each element of an array is capital using filter method. But I keep getting the whole array

// JavaScript Document
"use strict";
var testArray =["beau","Diane","morgan","Brittany"];
function checkCapital(value,index,array) {
if(value!==value.charAt(0).toLowerCase())
return value;
}
var capital =testArray.filter(checkCapital);
console.log(capital);
I need to check if the first letter of every value of the array is a capital using filter method. But I keep getting the whole array returned on my function.
Well, you are comparing an entire string with the lowercase equivalent of its first character, which is a problem. And you are going to return undefined when your condition is not satisfied, and a specific character if it is, which is a little strange. Your filter function should be
return value.charAt(0) !== value.charAt(0).toLowerCase()
or use the more modern
return value[0] !== value[0].toLowerCase()
and write the whole app in more modern JavaScript as follows:
const testArray = ["beau", "Diane", "morgan", "Brittany", "^&#SYMBOLS$$--"];
const capital = testArray.filter(s => s[0] !== s[0].toLowerCase());
console.log(capital);
but really, what is and is not an uppercase letter is an interesting problem in its own right. What if a word starts with a comma? A Cherokee or Devanagari letter? Letter-like symbols? If you really want to do uppercase properly, you might want to get a package like xregexp then you can write a filter function that matches XRegExp("^\\p{Lu}"). I understand this might be beyond what you are looking for at the moment, but it's a good thing to know about.
The filter() method depends on the return statement of the callback, which if returns true then filter() will include the current value to the array it will return. Just remove your if statement and put the condition on the return statement:
function checkCapital(value,index,array) {
return value !== value.charAt(0).toLowerCase();
}
Because your original callback returns true always, it would include all elements of the original array to be returned by the filter() method.
First off you are always returning value from your filter function, that is the core reason why you are getting the whole array. A filter function shold return true or false depending on whether the value should or should not be included in the output.
You have other issues with your code but try and solve them on your own.
solution :-
function checkUppercase(arr)
{
var array1 = arr.split(' ');
var newarray1 = [];
for(var x = 0; x < array1.length; x++){
newarray1.push(array1[x].charAt(0).toUpperCase()+array1[x].slice(1));
}
newarray1.join(' ');
let i=0;
if(JSON.stringify(array1) == JSON.stringify(newarray1)) {
return true
}else {
return false;
}
}
console.log(checkUppercase("solid Is Transparent"));

How can I check JavaScript arrays for empty strings?

I need to check if array contains at least one empty elements. If any of the one element is empty then it will return false.
Example:
var my_arr = new Array();
my_arr[0] = "";
my_arr[1] = " hi ";
my_arr[2] = "";
The 0th and 2nd array elements are "empty".
You can check by looping through the array with a simple for, like this:
function NoneEmpty(arr) {
for(var i=0; i<arr.length; i++) {
if(arr[i] === "") return false;
}
return true;
}
You can give it a try here, the reason we're not using .indexOf() here is lack of support in IE, otherwise it'd be even simpler like this:
function NoneEmpty(arr) {
return arr.indexOf("") === -1;
}
But alas, IE doesn't support this function on arrays, at least not yet.
You have to check in through loop.
function checkArray(my_arr){
for(var i=0;i<my_arr.length;i++){
if(my_arr[i] === "")
return false;
}
return true;
}
You can try jQuery.inArray() function:
return jQuery.inArray("", my_arr)
Using a "higher order function" like filter instead of looping can sometimes make for faster, safer, and more readable code. Here, you could filter the array to remove items that are not the empty string, then check the length of the resultant array.
Basic JavaScript
var my_arr = ["", "hi", ""]
// only keep items that are the empty string
new_arr = my_arr.filter(function(item) {
return item === ""
})
// if filtered array is not empty, there are empty strings
console.log(new_arr);
console.log(new_arr.length === 0);
Modern Javascript: One-liner
var my_arr = ["", "hi", ""]
var result = my_arr.filter(item => item === "").length === 0
console.log(result);
A note about performance
Looping is likely faster in this case, since you can stop looping as soon as you find an empty string. I might still choose to use filter for code succinctness and readability, but either strategy is defensible.
If you needed to loop over all the elements in the array, however-- perhaps to check if every item is the empty string-- filter would likely be much faster than a for loop!
Nowadays we can use Array.includes
my_arr.includes("")
Returns a Boolean
You could do a simple help method for this:
function hasEmptyValues(ary) {
var l = ary.length,
i = 0;
for (i = 0; i < l; i += 1) {
if (!ary[i]) {
return false;
}
}
return true;
}
//check for empty
var isEmpty = hasEmptyValues(myArray);
EDIT: This checks for false, undefined, NaN, null, "" and 0.
EDIT2: Misread the true/false expectation.
..fredrik
function containsEmpty(a) {
return [].concat(a).sort().reverse().pop() === "";
}
alert(containsEmpty(['1','','qwerty','100'])); // true
alert(containsEmpty(['1','2','qwerty','100'])); // false
my_arr.includes("")
This returned undefined instead of a boolean value so here's an alternative.
function checkEmptyString(item){
if (item.trim().length > 0) return false;
else return true;
};
function checkIfArrayContainsEmptyString(array) {
const containsEmptyString = array.some(checkEmptyString);
return containsEmptyString;
};
console.log(checkIfArrayContainsEmptyString(["","hey","","this","is","my","solution"]))
// *returns true*
console.log(checkIfArrayContainsEmptyString(["yay","it","works"]))
// *returns false*
yourArray.join('').length > 0
Join your array without any space in between and check for its length. If the length, turns out to be greater than zero that means array was not empty. If length is less than or equal to zero, then array was empty.
I see in your comments beneath the question that the code example you give is PHP, so I was wondering if you were actually going for the PHP one? In PHP it would be:
function hasEmpty($array)
{
foreach($array as $bit)
{
if(empty($bit)) return true;
}
return false;
}
Otherwise if you actually did need JavaScript, I refer to Nick Craver's answer
Just do a len(my_arr[i]) == 0; inside a loop to check if string is empty or not.
var containsEmpty = !my_arr.some(function(e){return (!e || 0 === e.length);});
This checks for 0, false, undefined, "" and NaN.
It's also a one liner and works for IE 9 and greater.
One line solution to check if string have empty element
let emptyStrings = strArray.filter(str => str.trim().length <= 0);
let strArray = ['str1', '', 'str2', ' ', 'str3', ' ']
let emptyStrings = strArray.filter(str => str.trim().length <= 0);
console.log(emptyStrings)
One line solution to get non-empty strings from an array
let nonEmptyStrings = strArray.filter(str => str.trim().length > 0);
let strArray = ['str1', '', 'str2', ' ', 'str3', ' ']
let nonEmptyStrings = strArray.filter(str => str.trim().length > 0);
console.log(nonEmptyStrings)
If you only care about empty strings then this will do it:
const arr = ["hi","hello","","jj"]
('' in arr) //returns false
the last line checks if an empty string was found in the array.
I don't know if this is the most performant way, but here's a one liner in ES2015+:
// true if not empty strings
// false if there are empty strings
my_arr.filter(x => x).length === my_arr.length
The .filter(x => x) will return all the elements of the array that are not empty nor undefined. You then compare the length of the original array. If they are different, that means that the array contains empty strings.
You have to check in through the array of some functions.
if isEmptyValue is true that means the array has an empty string otherwise not.
const arr=['A','B','','D'];
const isEmptyValue = arr.some(item => item.trim() === '');
console.log(isEmptyValue)
array.includes("") works just fine.
Let a = ["content1", "" , "content2"];
console.log(a.includes(""));
//Output in console
true

Categories

Resources