Check if second string is rotation of another string - javascript

I'm trying to write a function that takes in two string parameters and check if the second parameter is a rotated version of the first string.
So the following would be the results:
checkRotationStrings('waterbottle', 'lewaterbott'); // true
checkRotationStrings('waterbottle', 'bottlewater'); // true
checkRotationStrings('waterbottle', 'erbottlewat'); // true
checkRotationStrings('waterbottle', 'lewaterbottx'); // false
I wrote the following code, but there are some edge cases that I can't seem to figure out:
function checkRotationStrings(string, rotatedString) {
let result;
let rotationCheck
let stringArr = string.split('');
let rotatedStringArr = rotatedString.split('')
for (let i = 0; i < string.length - 1; i++) {
if (rotatedString[0] === stringArr[i]) {
result = stringArr.slice(i);
rotationCheck = stringArr.slice(0, i).concat(result).join('');
}
}
console.log(rotationCheck)
if (rotationCheck === string){
return true;
} else {
return false;
}
}
Any help would be appreciated.

You could use String#repeat with rotated and two as parameter and check with String#includes.
function checkRotationStrings(string, rotated) {
return string.length === rotated.length && rotated.repeat(2).includes(string);
}
console.log(checkRotationStrings('waterbottle', 'lewaterbott')); // true
console.log(checkRotationStrings('waterbottle', 'bottlewater')); // true
console.log(checkRotationStrings('waterbottle', 'erbottllewat')); // false
console.log(checkRotationStrings('waterbottle', 'lewaterbottx')); // false
console.log(checkRotationStrings('waterbottle', 'ttlewaterb')); // false

You could use substring and rotate until you find a match. Like this:
function checkRotationStrings(string, rotatedString) {
let match = false;
for (let i = 0;
(i < string.length - 1) & !match; i++) {
match = rotatedString.substring(i, rotatedString.length) + rotatedString.substring(0, i) === string;
}
return match
}
console.log(checkRotationStrings('waterbottle', 'lewaterbott')); // true
console.log(checkRotationStrings('waterbottle', 'bottlewater')); // true
console.log(checkRotationStrings('waterbottle', 'erbottlewat')); // true
console.log(checkRotationStrings('waterbottle', 'lewaterbottx')); // false

This is a somewhat strange solution, as it uses some only for the index parameter. But each iteration in some simply compares two strings, one which is a rotation of the first, and the other is the second one.
const checkRotationStrings = (str, rot) =>
str.split('').some((s, i) => str.slice(i) + str.slice(0, i) == rot);
[
['waterbottle', 'lewaterbott'], // true
['waterbottle', 'bottlewater'], // true
['waterbottle', 'erbottllewat'], // false -- ORIGINAL
['waterbottle', 'erbottlewat'], // true -- CORRECTED
['waterbottle', 'lewaterbottx'] // false
].forEach(([s, r]) => console.log(`'${s}', '${r}': ${checkRotationStrings(s, r)}`))

You can do the following:
checkRotationStrings(str1: string, str2: string) {
if (str1.length !== str2.length) {
return false;
} else {
for (var i = 0; i < str2.length; i++) {
if (str2[i] === str1[0]) {
var substring1 = str2.substring(0,i);
var substring2 = str2.substring(i,str2.length);
var concatWord = substring2.concat(substring1);
if(str1 === concatWord){
console.log(str1 + " matches " + concatWord)
return true;
}else{
console.log(str1 + " not matches " + concatWord)
}
}
}
return false;
}
}

Stack Overflow questions often receive answers in the form of completely new code (and/or ideas) on how to achieve the desired result. Usually, there's at least an attempt to help the questioner with their actual question, which in this case seemed to ask for some help with your code (one issue at least had to do with "slicing," as you commented).
I also love to offer new code or ideas if I particularly like it so I'm only partly critical here, but the answers so far have suffered from a complete lack of relating to the question.
There's nothing wrong with how you have conceived of checking for string rotation. You just have a couple of bugs:
First, since rotationCheck is meant to rotate string in order to compare it with rotatedString, you got the string-building reversed. It should be:
rotationCheck = result.concat(stringArr.slice(0, i)).join('');
Secondly, once you've built the rotation-check, you need to compare it with rotatedString, not string. So:
if (rotationCheck === rotatedString){

I might be late for this, but here is my solution and it include all edge cases.
function checkRotation(str1,str2){
const char0 = str1[0];
let ind = str2.indexOf(char0);
while(ind>-1){
const start = str2.substring(0,ind);
const end = str2.substring(ind,str2.length);
if(end+start === str1){
return true;
}
ind = str2.indexOf(char0,ind+1)
}
return false
}
console.log(checkRotation("assads","adsass"))

Instead of trying to break it in random pieces and do different checks, you could instead do a count of the letters. This would be much simpler:
const countLetters = a => Array.prototype.reduce.call(a, (r, l) => Object.assign(r, { [l]: (r[l] || 0) + 1 }), {});
function checkRotatedString(a, b) {
const countA = countLetters(a);
const countB = countLetters(b);
return Object.keys(countA).length === Object.keys(countB).length
&& Object.entries(countA).every(([key, value]) => value === countB[key]);
}
// Tests
[['waterbottle', 'lewaterbott'], ['waterbottle', 'bottlewater'], ['waterbottle', 'erbottlewat'], ['waterbottle', 'lewaterbottx']]
.forEach(a => console.log(a, checkRotatedString.apply(null, a)));

Related

Checking if str1 can be re-arranged as str2 wth JavaScript

There are two strings called str1 and str2 and I'm trying to check if str1 can be re-arranged as str2.
FOR EXAMPLE: lets say str1 = "aabbcamaomsccdd" and str2="commas".
Is it possible to write the word "commas" out of "str1"
function scramble(str1, str2) {
let arr=[];
let str1arr = str1.split("");
let str2arr = str2.split("");
let j=0;
for(let i=0; i<str1.length; i++){
if(str1arr[i]==str2arr[j]){
arr.push(str1arr[i]);
str1arr=str1arr.splice(i,1);
j++;
i=0;
}
}if(arr.toString()===str2arr.toString()){
return true;
}else{
return false;
}
}
What I tried basically if str1arr[i]==str2arr[j] it will put the str1arr[i] value on a new array called arr and at the end it will compare str2 and the arr and return True or False.
The reason why I used str1arr=str1arr.splice(i,1); to delete the i after the match is because the for loop is reseting it self to check from the "i=0" each time i and j matches and that i would match with other duplicate letters (I hope thats what it does atleast :D).
It is an internet question and im not passing the tests. I only pass if the result is FALSE.
I want to know what I'm doing and thinking wrong here. Its not performance efficent too so any comment on that would be great too.
You could take arrays and sort them and check each character of the second string/array against the first one.
function compare([...a], [...b]) {
a.sort();
return b.sort().every((i => v => {
while (i < a.length && a[i] !== v) i++;
return a[i++] === v;
})(0));
}
console.log(compare("aabbcamaomsccdd", "commas")); // true
console.log(compare("aabbcamaomccdd", "commas")); // false
You should just check that both strings contain the same chars like so:
function scramble(str1, str2) {
var s1 = str1.split('');
var s2 = str2.split('');
var i;
for (i = 0; i < s2.length; i++) {
const idx = s1.indexOf(s2[i]);
if (idx === -1) {
return false;
}
s1.splice(idx, 1);
}
return s1.length === 0;
}
console.log(scramble('xcab1c', 'abxcc1'));
You could count the frequency of each character in your first string. Below I have used .reduce() to build an object with key-value pairs, where the key represents a character from your s1 string and the value is how many times it appears. You can then loop through the characters in s2 and check that every character appears in the frequency map. When you see a character you can subtract one from the value from the frequency object to signify that the character has been "used". If the .every() callback returns a falsy value (such as 0 for the value), then the result will be false, as your string can't be re-arranged:
const scramble = (s1, s2) => {
const s1Freq = [...s1].reduce((o, c) => ({...o, [c]: (o[c] || 0) +1}), {});
return [...s2].every(char => s1Freq[char]--);
}
console.log(scramble("aabbcamaomsccdd", "commas")); // true
console.log(scramble("abc321", "123")); // true
console.log(scramble("a3b2c11", "1231")); // true
console.log(scramble("a", "a")); // true
console.log(scramble("xyz", "xyt")); // false

check if text is a subset of a serie

I want to check a string against a serie (with respect to order)
-- see bellow for another approach (string solution)
Regex solution (potential)
r = /[(qw|az)ert(y|z)uiop(é|å|ü)]{5}/g
str = "ertyu"
str2 = "rtrrr"
r.test(str) // true
r.test(str2) // true
The str2 should return false as it does not respect the order, but since the regex is wrapped in array I assume this approach is wrong from the start.
String solution
arr = [
"qwertyuiopé",
"qwertyuiopå",
"qwertyuiopü",
"qwertzuiopé",
"qwertzuiopå",
"qwertzuiopü",
"azertyuiopé",
"azertyuiopå",
"azertyuiopü",
"azertzuiopé",
"azertzuiopå",
"azertzuiopü",
]
str = "ertyu"
str2 = "yrwqp"
function test(s) {
for (const a of arr) {
if (a.indexOf(str) >= 0) return true
}
return false
}
test(str) // true
test(str2) // false
The string version works but its ugly and big
Is there a way with regex to get this working?
In the end I don't think what I want to achieve would be possible,
I ended up with an array of different series (keyboard trivial series)
and a function that checks if a password is a sequence of the series
const trivialseries = [
// swedish, german, french, english, spanish, italian - keyboard
"1234567890",
"qwertyuiopå", // se|en
"asdfghjklöä",
"zxcvbnm",
"qwertzuiopü", // de
"yxcvbnm",
"azertyuiop", // fe
"qsdfghjklmù",
"wxcvbn",
"asdfghjklñ", // sp
"qwertyuiopé", // it
"asdfghjklòàù",
];
const MAX = 5;
function subsequence(serie, str) {
for (let i = 0; i < str.length - MAX + 1; i++) {
const index = serie.indexOf(str[i]);
if (index >= 0) {
let found = 1;
for (let j = i + 1; j < str.length; j++) {
if (serie[index + found] === str[j]) found++;
else break;
if (found === MAX) return true;
}
}
}
return false;
}
function isTrivial(password) {
for (let ts of trivialseries) {
if (subsequence(ts, password)) return true;
const reverse = ts.split("").reverse().join("");
if (subsequence(reverse, password)) return true;
}
return false;
}
console.log(isTrivial("e927ncsmnbvcdkeloD€%s567jhdoewpm")); // true "mnbvc" is reverse form of "cvbnm"

Javascript function that checks whether number is palindrome

I read through a few of palindrome questions posted here, but unfortunately couldn't find a way to fix mine. An example of what I'm trying to achieve:
Input: 989
Output: "It's a palindrome"
Input: 23
Output: "Not a palindrome"
Input: 9
Output: "It's a palindome" (any single digit)
My try
function Palindrome(num) {
let numToStringArray = num.toString().split('');
let reversedArray = numToStringArray.reverse();
if (num.toString().length<2) {
return "It's a palindrome"
}
else {
for (let i = 0; i<numToStringArray; i++;) {
if (numToStringArray[i] !== reversedArray[i]) {
return "It's not a palindrome"
}
else {
return "It's a palindrome"
}
}
}
}
When invoked, the function only works for single-digit strings. I tried to fix my for-loop, as I feel that the problem lies in the following line:
if (numToStringArray[i] !== reversedArray[i])
but could not come up with a working solution. Thanks for reading or even helping me out!
I'm spotting several problems...
First, you don't want a ; after your i++ in the loop definition. In jsFiddle at least, that's resulting in a syntax error. Other environments may be more forgiving, but it's worth fixing.
Second, the loop condition is wrong:
i < numToStringArray
should be:
i < numToStringArray.length
Third, the logic of the loop is a bit broken. You're returning "It's a palindrome" immediately if the very first pair match. So by that logic "1231" is a palindrome. Instead, only return within the loop if you find that it's not a palindrome. If the loop completes without returning, then return that it's a palindrome:
for (let i = 0; i < numToStringArray.length; i++) {
if (numToStringArray[i] !== reversedArray[i]) {
return "It's not a palindrome";
}
}
return "It's a palindrome";
Made this quick & working solution:
function checkPalindrome(num) {
var numString = num.toString();
return numString.split("").reverse().join("") == numString;
}
Provide an integer parameter inside the checkPalindrome() function, and it will return either true or false.
For example:
if (checkPalindrome(123321)) {
console.log("Is a palindrome");
} else {
console.log("Not a palindrome");
}
How about this
str === str.split('').reverse().join("")
like so
const palindrome = num => {
const str = num.toString();
return `It's ${str.length<2 || str === str.split('').reverse().join("") ? "" : "not "}a palindrome`
};
console.log(
[989, 23, 9].map(num => `${num}: ${palindrome(num)}`)
)
actually your numToStringArray is not reversing. Try this:
function Palindrome(num) {
let numToStringArray = num.toString().split('');
let reversedArray = num.toString().split('').reverse();
console.log("here", numToStringArray, reversedArray)
if (num.toString().length<2) {
return "It's a palindrome"
}
else {
for (let i = 0; i<numToStringArray.length; i++) {
if (numToStringArray[i] !== reversedArray[i]) {
return "It's not a palindrome"
}
else {
return "It's a palindrome"
}
}
}
}
console.log(Palindrome(686))
The reason your logic doesn't is mainly due to the fact that you use i < numToStringArray instead of i < numToStringArray.length as mentioned in the comments.
The simplest way to achieve what you want though, would simply be-
function isPalindrome(num) {
return num === Number(num.toString().split('').reverse().join(''));
}
This should work.
const pal = num => {
let reversedNum = parseFloat(num.toString().split('').reverse().join(''));
if (reversedNum === num) {
console.log("palindrome")
} else {
console.log("no palindrome")
}
}
pal(12121);
Without using split, reverse and join, it can be checked through a single array.
function Palindrome(num) {
let numToStringArray = num.toString();
var len = numToStringArray.length;
if (len < 2) {
return "It's a palindrome"
}
else {
for (var i = 0; i < len / 2; i++) {
if (numToStringArray[i] !== numToStringArray[len - 1 - i]) {
return "It's not a palindrome";
}
return "It's a palindrome"
}
}
}
console.log(Palindrome(989));
console.log(Palindrome(23));
console.log(Palindrome(9));
A short function to test if the number is a Palindrome and returns a "true" or "false" is as follows:
You can then call it and output your text result "It's a palindrome" in case it returns true or "Not a palindrome" if returns false.
Test examples provided below.
const IsPalindrome = e => (e+="").split("").reverse().join("") == e;
// ======== test =============
console.log(IsPalindrome(23)); // false
console.log(IsPalindrome(121)); // true
console.log(IsPalindrome(9889)); // true
console.log(IsPalindrome(989)); // true
console.log(IsPalindrome(1)); // true
To meet your specific requirements for the output text. Here is a one-liner function based on the function below:
function IsPalindrome(n) {
return n + (((n+="").split("").reverse().join("") == n) ? " is a palindrome" : " is not a palindrome");
}
// ======== test =============
console.log(IsPalindrome(23)); // 23 is not a palindrome
console.log(IsPalindrome(121)); // 121 is a palindrome
console.log(IsPalindrome(9889)); // 9889 is a palindrome
console.log(IsPalindrome(989)); // 989 is a palindrome
console.log(IsPalindrome(1)); // 1 is a palindrome
Taking advantage of its function, something you could do after using the split method and the reverse method to read the number from right to left, is to use the join to transform it into a string again and then compare them, like this:
function Palindrome(num) {
let numToStringArray = num.toString().split('');
let reversedArray = numToStringArray.reverse().join('');
if (num.toString() == reversedArray)
console.log('It\'s a palindrome');
else
console.log('It\'s not a palindrome');
}
Palindrome(9);
Palindrome(232);
Palindrome(152);
let a, b = 121;
a = b;
console.log(String(b).split("").reverse().join() === String(a).split("").join());

See if the end of strings match

I have this exercise: Check if a string (first argument, str) ends with the given target string (second argument, target) without using the method endsWith() or any other method. What's wrong with my code?
function confirmEnding(str, target) {
for (var i = 1; i <= target.length; i++) {
val = false
if (str[str.length - i] === target[target.length - i]) {
val = true;
}
return val;
}
}
confirmEnding("Bastian", "n");
//original code from post above:
console.log(confirmEnding("Bastian", "n")); //added to provide working example
In your original code there are a few problems:
Will address them inline:
function confirmEnding(str, target) {
// using a for loop to iterate over the target string's length
for (var i = 1; i <= target.length; i++) {
//setting up a variable that says false
val = false
//trying to compare the individual characters
if (str[str.length - i] === target[target.length - i]) {
//so what happens here:
//when the two match this will set val to true
//but every time the loop is run is will reset to false.
val = true;
}
//the return value is in the loop, so the loop will run once
return val;
}
}
confirmEnding("Bastian", "n");
With the above script you have no way of knowing if all the characters match. If the last character matches it will return true, even if the other characters don't match.
string: Bastian target: irr
Will return true in the logic of your loop.
Take a look at the code below and the comments in it!
function confirmEnding(str, target) {
//get the length of the target string
const targetLength = target.length;
//set up an empty string
let endstr = "";
for (let i = 1; i <= targetLength; i++)
{
//start at 1 since str.length-1 is last character
//fill the empty string with the last characters of str
endstr = str[str.length-i] + endstr;
}
//compare and return
return target === endstr;
}
console.log(confirmEnding("Bastian", "ian")); //TRUE
console.log(confirmEnding("Bastian", "in")); //FALSE
The problem in your current code is that the variable for the result is initialised inside the loop.
So it actually only returns true/false depending on the last character it compares.
Which would be the first character of the target string.
What you can do is get out of that loop as soon a difference is found.
And additional, also check if the string isn't smaller than the target, because then it would be false anyway.
F.e.
function confirmEnding(str, target) {
if(str.length < target.length)
return false;
for (var i = 1; i <= target.length; i++) {
if (str[str.length - i] !== target[target.length - i]) {
return false;
}
}
return true;
}
console.log(confirmEnding("Bastian", "ian"));
console.log(confirmEnding("Bastian", "ion"));
console.log(confirmEnding("an", "ian"));
const confirmEnding = (str,target) => str.slice(-target.length) === target;
console.log(confirmEnding('peace','ace')); // true
console.log(confirmEnding('devotion','tio')); // false
console.log(confirmEnding('faith and love','nd love')); // true
we know that "slice" take 2 arguments: [ index | how many items ]
more so, a negative index would start extracting characters from the end of a string
we can use this to our advantage by utilizing the "length" property from "target" to set the "index" on the "slice" method
let's use "peace" as our example to breakdown the code:
('peace','e') => str.slice(-1) === 'e';
str.slice(-1) --> return the last character 'e' from "peace"
('peace','e') => 'e' === 'e'; --> "true"

Javascript How to identify if all elements of one array are present in another

I need to create a function to check if all the letters in the second string of a two string array are present in the first string. The function I wrote seems to work for most of the examples I tried with it but ["hello" , "hey"] returns true despite there not being a y in hello and I don't understand why.
Here's my code:
function mutation(arr) {
arr[0] =arr[0].toUpperCase().split("");
arr[1] =arr[1].toUpperCase().split("");
for(i=0;i<arr[1].length;i++){
if(arr[0].indexOf(arr[1][i])>=0){
return true;
} else {return false;}}}
mutation(["hello", "Hey"]);
You are returning true even if one character is matched ,Try below code it checks if all characters are present or not
function mutation(arr) {
arr[0] = arr[0].toUpperCase().split("");
arr[1] = arr[1].toUpperCase().split("");
var count = 0;
for (i = 0; i < arr[1].length; i++) {
if (arr[0].indexOf(arr[1][i]) >= 0) {
count++;
}
}
return count === arr[1].length
}
mutation(["hello", "Hey"]);
here is one more efficient solution, it works only for lowercase letters.
(function(){
function charCode(str, i){
return str.charCodeAt(i) - 97;
}
function isMutation(a,b){
const aArr = new Uint8Array(26);
const bArr = new Uint8Array(26);
let i=0;
let index = 0;
while(i<a.length){
++aArr[charCode(a, i)];
++i;
}
i = 0;
while(i<b.length){
++bArr[charCode(b, i)];
++i;
}
i = 0;
while(i < 26){
if(!(aArr[i]===0 && bArr[i]===0 || aArr[i]>0 && bArr[i]>0)){
return false
}
++i;
}
return true;
}
console.assert(isMutation('hello', 'oleh') === true);
console.assert(isMutation('hello', 'hey') === false);
})();
you can also compare sum of uniq chars in the both arrays, but in this case you have to add each letters only once.
I would recommend using one of the code solutions suggested by user georg at Remove Duplicates from JavaScript Array.
For example, the function below could be used to sort each array (arr[0] and arr[1]) and remove duplicates.
Credit to user georg at the link above.
function uniq(a) {
return a.sort().filter(function(item, pos, ary) {
return !pos || item != ary[pos - 1];
})
}
Once you have sorted/removed duplicates, you can test to see if the two returned strings are equal or not.
Hello => EHLO, and Hey => EHY
EHLO !== EHY

Categories

Resources