Checking if str1 can be re-arranged as str2 wth JavaScript - 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

Related

How to build a function that searches for string occurrences?

I need help Writing a function subLength() that takes 2 parameters, a string and a single character. The function should search the string for the two occurrences of the character and return the length between them including the 2 characters. If there are less than 2 or more than 2 occurrences of the character the function should return 0. How can I solve this problem using loops?
subLength('Saturday', 'a'); // returns 6
subLength('summer', 'm'); // returns 2
subLength('digitize', 'i'); // returns 0
subLength('cheesecake', 'k'); // returns 0
Here I loop through the characters of the string to find each value that is the char.
if the length isn't 2, return 0.
using slice, get only the characters within the two found indexs and get that length adding one to fix the offset
const subLength = (str, char) => {
let strChars = str.toLowerCase().split(""),
found = [],
length = 0;
strChars.forEach((val, index) => {
if (val === char) {
found.push(index);
}
});
if (found.length != 2) {
return length;
}
return str.slice(found[0], found[1]).length + 1;
}
console.log(subLength('Saturday', 'a')); // returns 6
console.log(subLength('summer', 'm')); // returns 2
console.log(subLength('digitize', 'i')); // returns 0
console.log(subLength('cheesecake', 'k')); // returns 0
You can try this logic:
Loop over string and count number of occurance
if count is 2,
Create a regex to capture the string in between.
Return its length
Else return 0
function subLength(str, char) {
let length = 0;
const occuranceCount = Array
.from(str)
.filter((c) => c.toLowerCase() === char.toLowerCase())
.length
if (occuranceCount === 2) {
const regex = new RegExp(`${char}(.*)${char}`)
length = str.match(regex)[0].length
}
console.log(length)
return length;
}
subLength('Saturday', 'a'); // returns 6
subLength('summer', 'm'); // returns 2
subLength('digitize', 'i'); // returns 0
subLength('cheesecake', 'k'); // returns 0
Using just for loop:
function subLength(str, char) {
let count = 0;
let initPosition;
let lastPosition;
for (let i = 0; i < str.length; i++) {
if (str[i] === char) {
count++
if (count > 2) {
return 0;
}
if (initPosition === undefined) {
initPosition = i
} else {
lastPosition = i+1
}
}
}
return count < 2 ? 0 : lastPosition - initPosition;
}
console.log(subLength('Saturday', 'a')); // returns 6
console.log(subLength('summer', 'm')); // returns 2
console.log(subLength('digitize', 'i')); // returns 0
console.log(subLength('cheesecake', 'k')); // returns 0
I too am going through the Codecademy course where this question came up which led me to this post.
Using the RegExp solution provided by #Rajesh (thank you!!) I started to break it down to better understand what was going on and making notes/comments because I am still pretty new and haven't used or been exposed to some of these things.
At the end of it all I thought I'd share what I ended up with in case anyone found it helpful.
function subLength(str, char) {
// Outputting to the console what we are looking for given the value of the string and character from the test cases at the end of this script.
console.log(`Showing the subLength for the string: "${str}" between "${char}" and "${char}" including the "${char}" positions.`);
// create the length variable which will be returned by the function
let length = 0;
// ** Search the string for the two occurrences of the character and count them. Then assign the result to the occurrenceCount variable for use in the if else statement.
// The "Array" class is a global object that is used in the construction off arrays.
// The Array.from() static method creates a new, shallow-copied Array instance from an array-like or iterable object.
// The Array.filter() method creates a new array with all elements that pass the test implemented by the provided function. The "c" represents each element of the array/string which is then compared to the char variable. if it is a match it gets added to the Array. We use .toLowerCase on both to ensure case compatibility.
// Appending the Array with ".length" assigns occurrenceCount the numeric value of the array's length rather than the array of characters.
const occurrenceCount = Array.from(str).filter((c) => c.toLowerCase() === char.toLowerCase());
console.log(' The contents of the occurrenceCountArray = ' + occurrenceCount);
console.log(' The character occurrence count = ' + occurrenceCount.length);
// if the string has two occurrences : return the length between them including the two characters : else the string has less than 2 or more than 2 characters : return 0.
if (occurrenceCount.length === 2) {
// The RegExp object is used for matching text with a pattern. The "(.*)" in between the ${char}'s will match and capture as much as possible aka greedy match. "()" = capture anything matched. (" = start of group. "." = match any character. "*" = Greedy match that matches everything in place of the "*". ")" = end of group.
const regex = new RegExp(`${char}(.*)${char}`);
// log to console the pattern being matched
console.log(` regex pattern to find = ${regex}`);
// log to the console the [0] = index 0 pattern that was captured from the string using str.match(regex)[0]
console.log(` regex output = ${str.match(regex)[0]}`);
// Use".length" to count the number of characters in the regex string at index 0 of the regex array and assign that value to the length variable.
length = str.match(regex)[0].length;
// Output the results to the console
console.log(` The distance from "${char}" to "${char}" (including the "${char}" positions) in the string: ${str} = ${length}\n`);
// return the length value
return length;
} else {
// Output the results to the console
console.log(` The string either has too many or too few occurrences.\n The subLength = ${length}\n`);
// return the length value
return length;
}
}
// test cases
subLength('Saturday', 'a'); // returns 6
subLength('summer', 'm'); // returns 2
subLength('digitize', 'i'); // returns 0
subLength('cheesecake', 'k'); // returns 0
The answer I am getting is this:
const subLength = (str, char) => {
let charCount = 0;
let len = -1;
for (let i=0; i<str.length; i++) {
if (str[i] == char) {
charCount++;
if (charCount > 2) {
return 0;
}
if (len == -1) {
len = i;
} else {
len = i - len + 1
}
}
}
if (charCount < 2) {
return 0;
}
return len;
};
It is better to try yourself a solution first. It is a very bad practice to just ask a solution for your homework!!!
Even if the solution can be JUST a few lines of code i wrote for you with commments a working solution :
const subLength = (str,char) => {
// create an empty array
const strarr = [];
// push string into array
strarr.push(str);
//initiate a count variable
let count = 0;
// WRITE YOUR REGULAR EXPRESSION
// Using the regular expression constructor - new RegExp("ab{2}", "g") .
const regString = `[${char}]`;
const regex = new RegExp(regString, "g");
// iterate through the string array to
for (let i = 0; i < strarr.length; i++) {
// calculate how many time the character occurs
count = (strarr[i].match(regex) || []).length;
};
// check with if condition
//if count is 2
if (count === 2) {
// calculate the index of first ocurrance of the string
first = str.indexOf(char);
// calculate the index of second ocurrance of the string
second = str.lastIndexOf(char);
// calculate the distance between them
return second - first + 1;
// if count is greater than two return 0
}
else if (count > 2) {
return count = 0;
}
// if count is less than two return 0
else if (count < 2) {
return 0;
}
};
console.log(subLength("iiiiliiile","l"));
I just answered this problem in codeAcademy and this is the solution that I came up with, just using if-statements and string.indexOf
const subLength = (strng, char) => {
let firstIndex = strng.indexOf(char);
let secondIndex = strng.indexOf(char, (firstIndex + 1));
let thirdIndex = strng.indexOf(char, (secondIndex + 1));
if (firstIndex === -1){
return 0
} else if (secondIndex === -1){
return 0
} else if (thirdIndex === -1 ){
return (secondIndex - firstIndex + 1)
} else {
return 0
};
};

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"

Determine if an array has repeated element by sorting the array

<script>
var arr = [];
function repeater(str){
for (var i=0; i<str.length;i++)
{arr.push(str[i])}
arr.sort()
for (var g=0;g<arr.length;g++) {
if (arr[g]==arr[g+1])
{return false}
else {return true}
}
}
document.write(repeater("jrtgrt"))
console.log(arr)
</script>
Create a function that takes a string and returns either true or false depending on whether or not it has repeating characters.
The array is working by console, but the second part doesn't seem to be running.
Your loop is terminating after the first comparison because either way return is called.
<script>
var arr = [];
function repeater(str) {
for (var i = 0; i < str.length; i++) {
arr.push(str[i])
}
arr.sort()
for (var g = 0; g < arr.length - 1; g++) {
console.log(arr[g], arr[g + 1])
if (arr[g] == arr[g + 1]) {
return true
}
}
return false;
}
document.write(repeater("12s35sd46"))
console.log(arr)
</script>
There are a few ways to simplify your code.
You don't need a for loop to create an array from a string. You can use the split function.
arr = str.split("");
Your entire function can literally be simplified to this using a regular expression.
function repeater(str) {
return /(.).*\1/.test(str);
}
Here's an explanation of each part of the regular expression:
(.) any character, capture to use later
.* any character any number of times
\1 first captured character
An approach might not necessarily rely on an array's sort method. One, for instance, could stepwise shorten the list of characters and then, via an array's some method, determine whether the current character has a duplicate counterpart in the yet remaining list of characters. Thus one also keeps the amount of iteration cycles low ... example ...
function hasDuplicateChars(stringValue) {
var hasDuplicate = false;
var charList = stringValue.split('');
var char;
// continue taking the first entry of `charList` while mutating the latter.
while (!hasDuplicate && (char = charList.shift())) { // `char` either will be a string or a undefined value.
hasDuplicate = charList.some(function (listItem) { return (char === listItem); })
}
return hasDuplicate;
}
console.log('hasDuplicateChars("") ? ', hasDuplicateChars(""));
console.log('hasDuplicateChars("x") ? ', hasDuplicateChars("x"));
console.log('hasDuplicateChars("abcdefghijklmnopqrstuvwxyz") ? ', hasDuplicateChars("abcdefghijklmnopqrstuvwxyz"));
console.log('hasDuplicateChars("TheQuickBrownFox") ? ', hasDuplicateChars("TheQuickBrownFox"));
console.log('hasDuplicateChars("Hallo, world.") ? ', hasDuplicateChars("Hallo, world."));
console.log('hasDuplicateChars(" ") ? ', hasDuplicateChars(" "));
.as-console-wrapper { max-height: 100%!important; top: 0; }

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

How to write a character matching algorithm in JavaScript?

Given this input s1 = "dadxx" s2 = "ddxx" I'd expect the output to contain a bunch of a,b pairs wherever each character in s1 matched a character in s2 and vice versa (duplicates allowed). Among those pairs would be 0,0 because s1[0] and s2[0] are both equal to d.
The problem is that my output doesn't contain 2,1 even though s1[2] and s2[1] are both equal to d.
Can someone fix my algorithm or make a better one?
Here's a JSFiddle if it helps.
Here's my code:
// For each char, see if other string contains it
s1 = 'dadxx'
s2 = 'ddxx'
matchChars(s1,s2)
matchChars(s2,s1)
function matchChars(a,b) {
for (i = 0; i < a.length; i++) {
found = b.indexOf(a[i])
if (found >= 0) {
if (a===s1) console.log(i,found)
else console.log(found,i)
}
}
}
I believe the problem you're having is that you're only checking for a single match for s1[i] in s2 by using indexOf. That will find the first index of a matched value, not every index.
If you instead iterate through both strings and compare every character, you get the result I think you're trying to achieve.
// Define strings
s1 = 'dadxx'
s2 = 'ddxx'
matchChars(s1,s2)
matchChars(s2,s1)
function matchChars(a,b) {
// Convert strings to lower case for case insensitive matching
// Remove if case sensitive matching required
a = a.toLowerCase();
b = b.toLowerCase();
// Iterate through every letter in s1
for (i = 0; i < a.length; i++) {
// Iterate through every letter in s2
for (j = 0; j < b.length; j++) {
// Check if the letter in s1 matches letter in s2
if (a[i] === b[j]) {
// Changed per request of OP
(a === s1) ? console.log(i, j) : console.log(j, i);
// console.log([i, j]);
}
}
}
}
Working JSBin example: https://jsbin.com/wecijopohi/edit?js,console
You say duplicates are allowed but not required. I'm submitting this as a more modern approach, not as a correction to the accepted solution, which looks good to me. https://jsfiddle.net/avc705zr/3/
match = (a, b) => {
let re, match, matches = []
a.split('').forEach((l, i) => {
re = new RegExp(l, 'g')
while ((match = re.exec(b)) != null) {
matches.push([i, match.index])
}
})
return matches
}
However, in my experience when you actually need functionality like this, you only need one of the strings to exhausted. In other words, you are looking for matches in string 2 of all instances in string 1 -- which is to say, unique characters in string 1. So a modification which might come up in the real world might instead be like:
Array.prototype.unique = function() {
return this.filter(function (value, index, self) {
return self.indexOf(value) === index;
});
}
match = (a, b) => {
let re, match, matches = []
a.split('').unique().forEach(l => {
re = new RegExp(l, 'g')
while ((match = re.exec(b)) != null) {
matches.push([l, match.index])
}
})
return matches
}

Categories

Resources