Permutations filtered with no repeating characters - javascript

This is a task from freeCodeCamp.
My goal is to create a function which:
Takes any string with any characters.
Creates an array with all the permutations possible out of that string.
Filters the array and returns only the strings which don't have repeated consecutive letters.
Return the number of total permutations of the provided string that don't have repeated consecutive letters. Assume that all characters in
the provided string are each unique. For example, aab should return 2
because it has 6 total permutations (aab, aab, aba, aba, baa, baa),
but only 2 of them (aba and aba) don't have the same letter (in this
case a) repeating.
I can't figure out what have i wrote wrong. I think the problem lies either in the filter function or the permutation list is faulty.
function permAlone(str) {
if (str.length == 1) {
return str;
}
// Creates all possible Permutations and pushes to an array
var arr = [];
var p = 0; // position of the element which needs to be swapped
// Loop count equal to number of Permutations.
var loops = factorialize(str.length);
for (var i = 0; i < loops; i++) {
// if the position is not the last element in the strig keep swapping with next following character
if (p != str.length - 1) {
var splitStr = str.split('')
arraySwapElements(splitStr, p, p + 1);
str = splitStr.join('');
arr.push(str);
p += 1;
// when position is at the last index, change position to 0
} else {
p = 0;
i -= 1;
}
}
// swaps 2 items in an array
function arraySwapElements(arr, a, b) {
var item = arr[a];
arr[a] = arr[b];
arr[b] = item;
};
// outputs a factorial of a number
function factorialize(num) {
if (num === 0) {
return 1;
} else {
return num * factorialize(num - 1);
}
}
// filters any array which has 2 or more repeating characters
var x = arr.filter(function(str) {
var re = /(.)\1+/;
var result = re.test(str);
if (!result) {
return str;
}
})
// returns the filtered arrays length
return x.length
}
console.log(permAlone('abfdefa'));
When testing:
permAlone("aab") should return a number. // Correct
permAlone("aab") should return 2. // Correct
permAlone("aaa") should return 0. // Correct
permAlone("aabb") should return 8. // Correct
permAlone("zzzzzzzz") should return 0.// Correct
permAlone("a") should return 1.// Correct
permAlone("aaab") should return 0.// Correct
permAlone("abcdefa") should return 3600. // Incorrect
permAlone("abfdefa") should return 2640.// Incorrect
permAlone("aaabb") should return 12. // Incorrect

The issue stems from the logic used in the for loop. While the loop does generate the right number of total permutations, it doesn't generate all permutations.
For example, if our string to be permuted was "abcd", the swapping mechanism would generate strings like this:
bacd bcad bcda
cbda cdba cdab
dcab dacb dabc
adbc abdc abcd
Uh oh! That last arrangement is the same as the starting string. When we start swapping again, we're going to get the same set that we did on the first pass. We're never going to get a permutation like "acbd". Thus the resulting array contains higher numbers of some permutations and lower numbers of others.
I'm not sure how to fix it with the approach you're using, but a recursive function to get permutations could be written like this:
// Returns an array of all permutations of a string
function getPerms(str) {
// Base case. If the string has only one element, it has only one permutation.
if (str.length == 1) {
return [str];
}
// Initialise array for storing permutations
let permutations = [];
// We want to find the permutations starting with each character of the string
for (let i = 0; i < str.length; i++) {
// Split the string to an array
let splitStr = str.split('');
// Pull out the character we're checking for permutations starting with
let currentElem = splitStr.splice(i, 1)[0];
// Get permutations of the remaining characters
let subPerms = getPerms(splitStr.join(''));
// Concat each of these permutations with the character we're checking
// Add them to our list
subPerms.forEach(function (combination) {
permutations.push(currentElem.concat(combination));
});
}
// return our list
return combinations;
}

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
};
};

how to build count and say problem in javascript

I am trying to solve the below problem in JavaScript
The count-and-say sequence is the sequence of integers beginning as follows:
1, 11, 21, 1211, 111221, ...
1 is read off as one 1 or 11.
11 is read off as two 1s or 21.
21 is read off as one 2, then one 1 or 1211.
Given an integer n, generate the nth sequence.
Note: The sequence of integers will be represented as a string.
Example:
if n = 2,
the sequence is 11.
So I want to create a function which pass N integer and gives it value
Here is my code:
let countAndSay = function (A) {
if (A == 1) return "1"
if (A == 2) return "11"
let str ="11"
if(A > 2){
// count
}
}
I don't understand the logic for how to build this.
You need to be able to dynamically determine the number and type of chunks a string has, which can be done pretty concisely with a regular expression. To come up with the string to deconstruct on index n, recursively call countAndSay to get the result of n - 1:
let countAndSay = function (count) {
if (count === 1) {
return '1';
}
const digitsArr = countAndSay(count - 1).match(/(\d)\1*/g);
// You now have an array of each chunk to construct
// eg, from 1211, you get
// ['1', '2', '11']
return digitsArr // Turn the above into ['11', '12', '21']:
.map(digitStr => digitStr.length + digitStr[0])
.join(''); // Turn the above into '111221'
};
console.log(
countAndSay(1),
countAndSay(2),
countAndSay(3),
countAndSay(4),
countAndSay(5),
);
Here's a function that generates the next string of numbers based on the previous string you feed it:
function next(s) {
s = s + "*"; // add a flag value at the end of the string
var output = "";
var j = 0;
for (var i = 1; i < s.length; i++) {
if (s.charAt(i) != s.charAt(i - 1)) { // if the character changes, concatenate the amount (i - j) and the digit
output += (i - j) + s.substring(j, j+1);
j = i;
}
}
return output;
}
Then you'd need to recursively run next N times.

Problems with finding unique values in regEx

I have a javascript regEx that is supposed to find all values with curly brackets around them eg {} and return a list of the unique values. I thought that it was working perfectly but I found that it doesn't work depending on the sequence of values.
For example: If the target document contains {lorem}{lorem}{ipsem}{ipsem} the script logs what's wanted [lorem, ipsem] but {lorem}{ipsem}{ipsem}{lorem} the script logs [lorem, ipsem,lorem]. What am I doing wrong!?
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var result = str.match(/{.*?}/g).map(function(val) {
return val.replace(/[\])}[{(]/g, "");
//return val.replace(/(^.*\[|\].*$)/g,'');
});
//The purpose of sort_unique is to find one of every value or string represented in an array
function sort_unique(arr) {
if (result.length === 0) return arr;
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
var ret = [arr[0]];
for (var i = 1; i < arr.length; i++) {
if (arr[i - 1] !== arr[i]) {
ret.push(arr[i]);
}
}
for (var index = 0; index < ret.length; index++) {
Logger.log(ret[index]);
}
return ret;
}
result = sort_unique(result);
Logger.log("Getting final result for front end....");
Logger.log(result);
return result;
}
I believe part of your problem is the sort method. If you replace
arr = arr.sort(function(a, b) {
return a * 1 - b * 1;
});
with
arr = arr.sort();
Then the function appears to work, at least on my side.
This will run in O(n log n) time. You can do better without sorting, if you store the values you've found so far in a map instead of an array. This would run in linear time.
(Also you'll want to replace if (result.length === 0) return arr; with if (arr.length === 0) return arr; just to make your sort_unique function completely independent of the surrounding function.)
The simplest method would be to use a Set. Store each of the regex matches in a set, then return Array.from(mySet).
var mySet = new Set();
str.match(/{.*?}/g).forEach(function(val) {
mySet.add(val.replace(/[\])}[{(]/g, ""));
});
return Array.from(mySet);
A set's add() function is O(1) so the total running time is O(n) where n is the number of matches in your string. Though, realistically, the regex search will be where most of the processing time occurs.
You check if the subsequent items are the same and those that are not subsequent land in the resulting array.
Check if the found value is in the result, and if not add the match, else, ignore.
Use the code like
function getVariables() {
var doc = DocumentApp.getActiveDocument();
var str = doc.getText(); //get the text of the document
var m, result=[], rx = /{([^{}]*)}/g;
while (m=rx.exec(str)) {
if (result.indexOf(m[1]) == -1) {
result.push(m[1]);
}
}
result.sort(); // If you really want to sort use this
// Logger.log(result); // View the result
}
The /{([^{}]*)}/g regex matches {, then captures into Group 1 zero or more chars other than { and }. So, the value you need is in m[1]. The if (result.indexOf(m[1]) == -1) checks if the value is in result.

Comparing values between two arrays

I'm trying to set up a function that checks if a word or a text is a palindrome. To do that, it splits the text so that every letter is an element of a new array, it takes rid of the white spaces and it makes the reverse array.
Then it checks if every element of the two arrays, at the same positions, are equal. If not it returns false, if yes it returns true.
Here the function:
function palindrome(str) {
var low = str.toLowerCase();
var newArray = low.split("");
var noSpace = newArray.filter(function(val) {
return val !== " ";
});
var reverse = noSpace.reverse();
function check (a, b) {
console.log(`checking '${a}' against '${b}'`);
var partial;
var result = 1;
for (var i = 0; i < a.length; i++) {
console.log(`comparing '${a[i]}' and '${b[i]}'`);
if (a[i] !== b[i]) {
result = 0;
} else {
partial = 1;
result *= partial;
}
}
return result;
}
var result = check(noSpace, reverse);
if (result == 1) {
return true;
} else {
return false;
}
}
palindrome("r y e");
I don't know what's wrong but it seems that the function keeps on returning a true value no matter what word or text I pass to the function. What is wrong with that?
Your issue seems to be because reverse() changes the actual array as well. So doing
var reverse = noSpace.reverse();
Will reverse noSpace and assign a reference to it on the variable reverse. That is, both arrays will be the same (reversed) array.
To bypass that, I've used .slice() to create a copy of the original array, and then called .reverse() on that new array, ridding you of any conflicts.
Here's a working snippet of what it looks like:
function palindrome(str) {
var str_array = str.toLowerCase().split("");
var no_space = str_array.filter(function(val) {
return val !== " ";
});
// By applying '.slice()', we create a new array
// reference which can then be reversed and assigned
// to the 'reverse' variable
var reverse = no_space.slice().reverse();
function check(a, b) {
var partial;
var result = 1;
for(var i=0; i < a.length; i++) {
if(a[i] !== b[i]) {
// We don't need to keep
// comparing the two, it
// already failed
return 0;
} else {
// I've kept this part even though
// I don't really know what it is
// intended for
partial = 1;
result *= partial;
}
}
return result;
}
return check(no_space, reverse) === 1;
}
console.log(palindrome("a b a"));
console.log(palindrome("r y e"));
The way you have coded for palindrome is way too complicated.
But there is one problem with your code: when you do a reverse() it changes the original array as well.
So you will need to make sure that you copy it via slice().
Also you can directly send a boolean result rather than doing a 1 and 0.
At result *= partial;, 1 * 1 will always equal 1
I didn't correct your code, but here is a optimized solution for you.
function palindrom(string) {
var arr = string.split("");
var lengthToCheck = Math.floor(arr.length / 2);
for (var i = 0; i < lengthToCheck; i++) {
if (arr[i] != arr[arr.length - (1 + i)]) {
return false;
}
}
return true;
}
First I split the array after every charater of the passed String. After that I get the half of the length of the array as it's enough to check just one half.
With the for-loop I compare the first half with the second half. As soon as I found two characters that do not match I return false. In case the whole first half matches the second half of the array, the for-loop will be completed and after that true will be returned.
What's actually happening is .reverse() reverses an array in place, it then stores a reference to that array which is not what you're calling in your check() method.
Simple fix would be to change your if statement:
if (a[i] !== b.reverse()[i])

Sorting odd and even numbers with the remainder operator Javascript

If a number, when divided by two, has a remainder that is not equal to 0, this number must be odd. I'm trying to use that logic in my if statement to keep only odd values, and get rid of even ones. I'm not sure how I'm doing this wrong, but myArray is returning even values as well as odd. Any ideas?
function sumFibs(num) {
var myArray = [1,1];
// Create fibonacci sequence
// Stop creating fibonacci numbers at num
// Push odd numbers to oddNums array
for (var i = 0; i < myArray.length; i++) {
if (myArray[i+1] + myArray[i] <= num && myArray[i+1] + myArray[i] % 2 !== 0) {
myArray.push(myArray[i+1] + myArray[i]);
}
} // End loop.
console.log(myArray);
// Summation of oddNums array.
return myArray.reduce(function(a,b) {
return a + b;
});
} // End function.
sumFibs(1000);
You are trying to filter odd values while generating your fib sequence, which probably not the best approach. If you wrap the modulo expression in parentheses,
(myArray[i+1] + myArray[i]) % 2
Your array will not contain the values necessary to continue generating the sequence. Ideally you should generate the full fib series and then filter:
var myArray = [1,1];
for (var i = 0; i <= num; i++) {
myArray.push(myArray[i+1] + myArray[i]);
} // End loop.
myArray = myArray.filter(function(a){ return a%2 !== 0 })
or save some reference to the even values so that they can be used to calculate the desired subset of the series.

Categories

Resources