How to tweak this function to make it passable? - javascript

I'm doing an interview prep challenge on FCC: https://www.freecodecamp.org/learn/coding-interview-prep/algorithms/no-repeats-please. The function takes a string of letters as an argument and creates all possible permutations of it, so abc becomes
abc, acb, bca, bac, cab, cba . It then returns the amount of permutations that don't have repeated consecutive letters in it, so aab returns 2. I wrote a function that does the job but does it so horribly that it still fails two of the conditions of the test, even though it still does return the correct results…after 46 seconds. But that’s for 7 letters, for a smaller number of letters it works ok. Some code editors will crash if you use 7 letters though, so use a sturdy one like Programiz if you want to run this code. My question is, can this code be modified to pass the test, while still preserving the logic?
function permAlone(arg) {
let length = arg.length
let arr = []
function uniqueIndices() { // generate a set of non-repeating integers to represent indices
let indices = new Array(length)
for (let i = 0; i < indices.length; i++) {
let r = Math.floor(Math.random() * length)
if (indices.indexOf(r) === -1) {
indices[i] = r
} else {
i--
}
}
return indices
}
function factorial(n) {
let product = 1;
for (let i = 2; i <= n; i++) {
product *= i;
}
return product;
}
let length2 = factorial(arg.length) // factorialize the length of arg to set the length of next loop which will be filled with sets of non-repeating integers
for (let i = 0; i < length2; i++) {
let val = uniqueIndices(arg)
if (arr.map(a => a.join('')).indexOf(val.join('')) === -1) { // make sure the sets of integers are all unique
arr[i] = val
} else {
i--
}
}
for (let indices of arr) { // now that we have the unique sets of non-repeating indices, convert each integer in the set to a letter in arg with the corresponding index
for (let i = 0; i < indices.length; i++) {
indices[i] = arg[indices[i]]
}
}
function final(a) { // final function to record the number of sets that have repeated consecutive letters
let ct = 0;
for (let x of a) {
for (let i = 0; i < x.length; i++) {
if (x[i] === x[i + 1]) {
ct++
break;
}
}
}
return ct
}
// to find the number of sets without repeated consecutive letters, substract the number of sets with repeated consecutive letters from arr.length
return arr.length - final(arr)
}
console.log(permAlone(['a', 'b', 'c', 'd', 'e']))

Related

Combine two small functions into one

So I am trying to "write a function Single() that takes a number and returns the number of times you must multiply the digits in num so as to reach a single digit. e.g. Single(456)//4*5*6=120, 1*2*0=0//it takes two loops to to reach the single digit so the return would be 2.
My problem is that now I have to put in an array of individual digits instead of a number. So I probably need to combine the two functions.
function numberToArray(num) {
var tmpString = num.toString();
var tempArray = [];
for (var i = 0; i < tmpString.length; i++) {
tempArray.push(tmpString[i]);
}
return tempArray;
}
function reachSingle(tempArray) {
var count = 0;
var k = 1;
for (var i = 0; i < tempArray.length; i++) {
k *= tempArray[i];
}
count++;
if (k <= 10) return count;
else {
var newArray = numberToArray(k);
return count + reachSingle(newArray);
}
}
document.write(reachSingle([2, 9, 3, 7, 6]));
You could take a recursive approach with an exit condition for numbers smaller than 10 and return in this case zero. Otherwise return one plus the result of the call of the product of the digits.
function reachSingle(n) {
if (n < 10) return 0;
return 1 + reachSingle(Array.from(n.toString()).reduce((a, b) => a * b));
}
console.log(reachSingle(29376)); // 4

How do I utilize the length of an array in a function (in JavaScript) when the array is being passed as a parameter and the length does not exist yet?

I'm trying to write a function that takes an array of strings(strarr) and an integer(k) as parameters and returns the longest string made up of k amount of consecutive strings within the array, not separated by commas. I need to call on strarr.length quite often during the function, but I keep getting an error saying that it cannot read that parameter's length.
This is my first time dealing with this issue and I have not found great internet search results for solutions. I suspect that I'm missing something very obvious. I have tried pushing the values of the strarr array parameter into a new array and still no luck.
So if I had const = ['apple','pineapple','banana','strawberry'] passed as my strarr parameter and 2 passed as k, then it should return 'bananastrawberry' because it is the longest consecutive pair of strings within the array.
const arr = ['apple', 'pineapple', 'banana', 'strawberry']
function longestConsec(strarr, k) {
if (strarr.length === 0) {
return "";
} else if (k > strarr.length) {
return "";
} else if (k <= 0) {
return "";
}
let longest = "";
let strLeng = 0;
for (let i = 0; i < strarr.length; i++) {
for (let j = i + (k - 1); j > 0; j--) {
strLeng += strarr[j].length;
}
if (strLeng > longest.length) {
longest = strarr.slice(i, (i + k)).join("");
}
}
return longest;
}
console.log(longestConsec(arr, 2))
As mentioned, you are trying to access an index that doesn't exist in you array.
A quick fix might be:
const arr = ['apple', 'pineapple', 'banana', 'strawberry']
function longestConsec(strarr, k) {
if (strarr.length === 0) {
return "";
} else if (k > strarr.length) {
return "";
} else if (k <= 0) {
return "";
}
let longest = "";
let strLeng = 0;
for (let i = 0; i < strarr.length; i++) {
for (let j = i + (k - 1); j > 0; j--) {
if (j >= strarr.length) {
break;
}
strLeng += strarr[j].length;
}
if (strLeng > longest.length) {
longest = strarr.slice(i, (i + k)).join("");
}
}
return longest;
}
console.log(longestConsec(arr, 2))
But I would suggest to see if there is better solution than adding a break statement.
Mistakes you did
In the inner for loop, for (let j = i + (k - 1); j > 0; j--), you're counting from i + k - 1. But what if i is the last index of array (strarr.length == 10 and i == 9) and k == 2? Then your loop starts at j = 9 + 2 - 1 = 10 and one line below, you try to do strLeng += strarr[10].length, but strarr[10] is not defined.
It also seems unnecessary to create strings before you're done finding the longest one. You could instead just remember start index of your last longest string instead.
How to make it better
Let's look at the requirements. For each i in the array, you want to merge k consecutive strings and keep the longest combination. From that follows:
i + k - 1 must never be larger than strarr.length. Since the i is the only variable here, we need to limit it by only looping up to strarr.length - k + 1.
If k == strarr.length, there is only one string you can make - strarr.join("")
Finally, there's an idea that you probably do not need nested loop at all. For every i, you simply subtract the length of the last string in your current window and add a new one. See image:
So with that in mind, I would propose following version of your code:
function longestConsec(strarr, k) {
// Cannot create any joined string with k greater than length
if(strarr.length < k) {
return "";
}
else if(k <= 0) {
return "";
}
else if(strarr.length == k) {
return strarr.join("");
}
let longestIndex = -1;
let longestLength = 0;
// length of our current group of strings
let currentLength = 0;
const maxLen = strarr.length;
for(let i=0; i<maxLen; ++i) {
// Forget the first strings length
if(i >= k) {
currentLength -= strarr[i-k].length;
}
// add the current strings length
currentLength += strarr[i].length;
// check if this is the largest length and save it's index
// Only possible after processing at least k strings
// Eg when i==1, we already went through 2 strings at this point
if(i >= k-1) {
if(currentLength > longestLength) {
const startIndex = i-k+1;
longestLength = currentLength;
longestIndex = startIndex;
}
}
}
return strarr.slice(longestIndex, (longestIndex + k)).join("");
}
Here's a jsFiddle test: https://jsfiddle.net/32g5oqd1/2/

Functions that takes array as argument and generate random numbers

First of all, any of built-in methods cannot be used. ex. pop(), shift(). What I can use is merely loops, array and so on.
I would like to make a function which takes an array as an argument and generate random strings of numbers, which does not contain these numbers given in the array.
For instance, func([6, 2]) //=> "20353" (2 and 6 would not be there).
The array length could change ([6, 2, 9], [7, 2, 1, 9]). So the function has to have an ability to accommodate any length of an array.
In order to tackle this practice question, I have used for and while loops. However, I ran into a problem that, when the second index is checked (whether numbers randomly generated contain 2 or not, in the example), if it contains, I regenerate the random number and it could produce the first index number (in this case, 6) which I do not want.
Please see the code I posted below and help me solve this. On top of that, if there is another way to get the same result which is a better way, please let me know too.
let str = "";
let arr = [];
let tem
const func = arg2 => {
for (let i = 0; i < 5; i++) {
arr[i] = Math.floor(Math.random() * 10);
}
for (let i = 0; i < arr.length; i++) {
for (let v = 0; v < arg2.length; v++) {
if (arg2[v] == arr[i]) {
do {
tem = Math.floor(Math.random() * 10);
} while (tem == arr[i])
arr[i] = tem;
}
}
}
for (let i = 0; i < arr.length; i++) str += arr[i]
return str
}
console.log(func([6, 2]))
// the output will not contain 2, which is the last index element
// however, when the second index number is removed, the output might replace it with 6, which is the first index element
Expected output:
func([6, 3, 8]) //=> "45102"
func([4, 9]) //=> "55108"
First, you already use two native methods (floor and random), but I'll assume you're OK with that.
Secondly, in your question the term digit would have been more appropriate in some instances than number. There is a difference...
To avoid that you still select a digit that is not allowed, you could first build an array with digits that are still allowed, and then randomly pick values from that array. That way you will not ever pick a wrong one.
Here is how that would look:
const func = arg2 => {
const digits = [0,1,2,3,4,5,6,7,8,9];
// Mark digits that are not allowed with -1
for (let i=0; i<arg2.length; i++) {
digits[arg2[i]] = -1;
}
// Collect digits that are still allowed
const allowed = [];
for (let i=0; i<digits.length; i++) {
if (digits[i] > -1) allowed[allowed.length] = digits[i];
}
// Pick random digits from the allowed digits
let str = "";
for(let i=0; i<5; i++) {
str += allowed[Math.floor(Math.random() * allowed.length)];
}
return str;
}
console.log(func([6, 2]));
Just for fun, if you lift the restrictions on what language aspects cannot be used, you can do this as follows:
const func = arg2 => {
const digits = new Set(Array(10).keys());
for (let digit of arg2) digits.delete(digit);
const allowed = [...digits];
return Array.from({length:5}, () =>
allowed[Math.floor(Math.random() * allowed.length)]
).join``;
}
console.log(func([6, 2]));
I suspect you're overthinking this. The basic algorithm is:
In a loop:
If output has 5 digits, return it.
Otherwise
Pick a random digit n from 0 to 9.
If n is not in the list of excluded numbers, it to output.
This maps pretty directly to the following function:
function fn(exclude, length = 5) {
let output = '';
while (output.length < length) {
const n = Math.floor(Math.random() * 10)
if (!exclude.includes(n)) {
output += n;
}
}
return output;
}
console.log(fn([6,3,8]));
There are, of course, other ways to achieve this, such as initializing an array with five elements and then joining the elements:
function fn(exclude, length = 5) {
return Array.from({ length }, () => {
let n;
while (n = Math.floor(Math.random() * 10), exclude.includes(n)) {}
return n;
}).join('');
}
console.log(fn([6,3,8]));
You need to loop through the entire arg2 array each time you pick a random digit. You can't replace the value in the arg2 loop, because then you won't check against earlier elements.
You don't need the arr array, you can append to str in the loop.
const func = arg2 => {
let str = "";
let arr = [];
for (let i = 0; i < 5; i++) {
let random;
while (true) {
let ok = true;
random = Math.floor(Math.random() * 10);
for (let j = 0; j < arg2.length; j++) {
if (random == arg2[j]) {
ok = false;
break;
}
}
if (ok) {
break;
}
}
str += random
}
return str
}
console.log(func([6, 2]))
Besides the fact (what others stated as well) that you use native array-methods yourself, I would probably go for it with something like this (using only what you used so far):
const func = without => {
let result = '';
while (result.length < 5) {
let rand = Math.floor(Math.random() * 10);
let add = true;
for (i=0; i<without.length; i++) {
if (rand === without[i]) {
add = false;
}
}
if (add) {
result += rand;
}
}
return result;
}
console.log(func([6, 2]))
a more concise version using native array methods could look like this:
const func = without => {
let result = '';
while (result.length < 5) {
let rand = Math.floor(Math.random() * 10);
if (!without.includes(rand)) {
result += rand;
}
}
return result;
}
console.log(func([6, 2]))

How to use array.push inside a function

I'm trying to return the array an array of numbers that conform to the two if statements. The prompt came from leet code, "Self Dividing Numbers", and asks to take in two arguments, a lower and upper bound and check if whether or not each number in that range is divisible by the digits of each individual number.
When I console.log(num) (the commented out portion, I get a correct list of numbers, but not in an array format. To fix this I thought to add a variable, result and return result after pushing an array to result inside the for loop. However when I do this, i only get the first correct term in an array, but not the full array.
How can this be fixed? I've tried moving the return statement in various locations, but that did not fix the issue.
The function should return [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 15, 22];
function selfDividingNumbers(left, right) {
for (let j = left; j <= right; j++) {
let num = j;
let result = []
let strNum = num.toString();
let dividingDigit = 0;
for (let i = 0; i < strNum.length; i++) {
if (num % parseInt(strNum[i]) == 0) {
dividingDigit++;
}
if (dividingDigit == strNum.length) {
result.push(num)
//console.log(num)
}
}
return result
}
};
console.log(selfDividingNumbers(1, 22));
From your expected output, define result at the very top of the function, and then return only after completely iterating through both loops:
function selfDividingNumbers(left, right) {
let result = []
for (let j = left; j <= right; j++) {
let num = j;
let strNum = num.toString();
let dividingDigit = 0;
for (let i = 0; i < strNum.length; i++) {
if (num % parseInt(strNum[i]) == 0) {
dividingDigit++;
}
if (dividingDigit == strNum.length) {
result.push(num)
//console.log(num)
}
}
}
return result
};
console.log(selfDividingNumbers(1, 22));
To be more concise, you might use .filter check whether .every digit divides evenly:
function selfDividingNumbers(left, right) {
return Array.from(
{ length: right - left },
(_, i) => i + left
)
.filter((num) => {
const digits = String(num).split('');
if (digits.includes(0)) {
return false;
}
return digits.every(digit => num % digit === 0);
});
}
console.log(selfDividingNumbers(1, 22));
When you declare let result = [] inside your for loop you are telling your code to recreate this array every time your loop iterates, thus, removing all previous results pushed into it. Instead, you need to move this outside your for loop to stop this from happening.
Lastly, you need to return only after you're outer for loop is complete, as returning inside your for loop will stop the function from running (and thus stop the loop).
See working example below:
function selfDividingNumbers(left, right) {
let result = [];
for (let j = left; j <= right; j++) {
let num = j;
let strNum = num.toString();
let dividingDigit = 0;
for (let i = 0; i < strNum.length; i++) {
if (num % parseInt(strNum[i]) == 0) {
dividingDigit++;
}
if (dividingDigit == strNum.length) {
result.push(num)
}
}
}
return result
};
console.log(selfDividingNumbers(1, 22));

How to code all for all cases of Two Sum javascript problem

I have been working on the two sum problem for the past few hours and can't seem to account for the case where there are only two numbers and their sum is the same as the first number doubled.
The result should be [0,1], but i'm getting [0,0].
let nums = [3,3];
let targetNum = 6;
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let b = i+1; b < nums.length; b++) {
if ((nums[i] + nums[b]) == target) {
return [nums.indexOf(nums[i]), nums.indexOf(nums[b])];
}
}
}
}
console.log(twoSum(nums, targetNum))
Two Sum
My approach uses a javascript object and completes the algorithm in O(n) time complexity.
const twoSum = (nums, target) => {
let hash = {}
for(i=0;i<nums.length;i++) {
if (hash[nums[i]]!==undefined) {
return [hash[nums[i]], i];
}
hash[target-nums[i]] = i;
}
};
console.log(twoSum([2,7,11,15], 9)); // example
This is not the way to solve the problem. Step through the array and save the complement of the target wrt the number in the array. This will also solve your corner case.
You should consider, indexOf(i) -> start from the first element, returns the index when match found! That is why in your code, nums.indexOf(nums[i]) and nums.indexOf(nums[b]) which is basically 3 in all two cases, it will return 0, cause 3 is the first element in array.
instead of doing this, return the index itself.
let nums = [3,3];
let targetNum = 6;
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let b = i+1; b < nums.length; b++) {
if ((nums[i] + nums[b]) == target) {
return i + "" +b;
}
}
}
}
console.log(twoSum(nums, targetNum))

Categories

Resources