I'm trying to create a recursive version of the following nested loops and to get the same results as the reference code. The example is below.
This is a version on Codepen http://codepen.io/anon/pen/XbQMLv
(The intent of the code is to output only unique combinations of integers from the indexes.)
Original code and output:
var len = 4;
for (var a = 0; a < len; a++) {
for (var b = a + 1; b < len; b++) {
for (var c = b + 1; c < len; c++) {
console.log(a, b, c);
}
}
}
// Outputs:
// 0 1 2
// 0 1 3
// 0 2 3
// 1 2 3
Recursive code and output:
var len = 4;
var end = 3;
var data = [];
var loop = function (index) {
if (index === end) {
console.log(data);
return;
}
for (var i = index; i < len; i++) {
data[index] = i;
loop(i + 1);
}
}
loop(0);
// Outputs:
// [ 0, 1, 2 ]
// [ 0, 2, 3 ]
// [ 1, 3, 2 ]
// [ 2, 3, 3 ]
Not sure what I'm missing here.
You have a single little error in your code:
You call a recursive function from your i + 1, but not your index + 1.
It causes index to be equal not current array index but it's value.
For example, when you passed [0, 1, 2], your data now is [0, 1] and you are about to insert 3, you call loop(3 + 1), index 4 goes out of an array range. if (index === end) condition fails and it doesn't output. for (var i = index; i < len; i++) loop fails as well, and everything is going wrong.
It should be:
var len = 4;
var end = 3;
var data = [];
var loop = function (index) {
if (index === end) {
console.log(data);
return;
}
for (var i = index; i < len; i++) {
data[index] = i;
loop(index + 1); // <--- HERE
}
}
loop(0);
Here is the working JSFiddle demo.
Update:
Oh, now I see. You need a[i] > a[i-1] condition to be true for all combinations. Just add a start variable which will save the last inserted value in order to comply with this rule.
var len = 4;
var end = 3;
var data = [];
var loop = function (start, index) {
if (index === end) {
document.body.innerHTML += "<br/>" + data;
return;
}
for (var i = start; i < len; i++) { // We start from 'start' (the last value + 1)
data[index] = i;
loop(i + 1, index + 1); // Here, we pass the last inserted value + 1
}
}
loop(0, 0); // At beginning, we start from 0
Updated JSFiddle demo with passing argument.
You can check the previous value instead of passing a value as an argument if it looks wrong for you. Condition will be like "if it is a first number, start from 0; else - start from the next number after the previous one".
var start = (index === 0 ? 0 : data[index-1] + 1);
Updated JSFiddle demo with calculating start.
There are a couple of errors; you are recursing starting from i+1 instead of index+1 and you are counting from index instead of counting from data[index-1]+1.
The corrected version is:
var len = 4;
var end = 3;
var data = [];
var loop = function (index) {
if (index === end) {
console.log(data);
return;
}
for (var i = (index==0 ? 0 : data[index-1]+1); i < len; i++) {
data[index] = i;
loop(index + 1);
}
}
As you are having three for loops one inside another, you have to pass 2 arguments for recursion function.
var len = 4;
var end = 3;
var data = [];
var loop = function(start, index) {
if (index === end) {
console.log(data);
return;
}
for (var i = start; i < len; i++) {
data[index] = i;
loop(i + 1, index + 1); //Pass as like a+1 & b+1
}
}
loop(0, 0);
Output the exact expected result.
var len = 4;
var end = 3;
var data = [];
var loop = function(i) {
if(data.length === end) {
// console.log(data); -> Wont work in snippet
// Snippet workaround
document.getElementsByTagName('body')[0].innerHTML += data.join(',') + '<br/>';
return;
}
if(i >= len)
return;
data.push(i);
loop(i + 1);
data.pop();
loop(i + 1);
};
loop(0);
Related
I want to write a Generator for Fibonacci numbers in Javascript;
0,1,1,2,5,7,12..... (to make the sequence you have to add the last two numbers)
But I have this problem when I assign the the output.length to a variable the code is not working, if I write it down straight instead of "newNumber" the code down is however working, but I don't understand what is wrong with the first one. Is it something wrong with the place of the variables?
function fibonacciGenerator(n) {
var output = [];
var lastNumber = output[output.length - 1];
var nPrev = output[output.length - 2];
var newNumber = lastNumber + nPrev;
if (n === 1) {
output = [0];
} else if (n === 2) {
output = [0, 1];
} else {
output = [0, 1];
for (var i = 2; i < n; i++) {
output.push(newNumber);
}
}
return output
}
console.log(fibonacciGenerator(5));
function fibonacciGen(n) {
const output = [0, 1];
// Return an empty array if n is less than 1
if (n < 1) {
return [];
}
// If n is 1 or 2, we can return the array now
if (n <2) {
return output;
}
// Loop through the remaining numbers in the sequence
for (let i = 2; i < n; i++) {
// Calculate the next number in the sequence
let lastNumber = output[output.length - 1];
let nPrev = output[output.length - 2];
let newNumber = lastNumber + nPrev;
// Add the new number to the output array
output.push(newNumber);
}
// Return the output array
return output;
}
console.log(fibonacciGen(5));
you have to declare your logic of next term in loop because first time length of output is zero
function fibonacciGenerator (n) {
var output =[];
if (n < 1) {
return [];
}
if (n===1){
output=[0];
}
else if (n===2){
output=[0,1];
}
else{
output=[0,1];
for( var i = 2; i < n; i++){
var lastNumber=output[output.length-1];
var nPrev=output[output.length-2];
var newNumber=lastNumber+nPrev;
output.push(newNumber);
}
}
return output
}
function tribonacci(signature, n) {
var myArray = [];
var lenArray = 0;
var i = 0;
var outPut = 0;
var x = 0;
if (n > 0) {
while (i < 3) {
myArray.push(signature[i]);
i++;
}
lenArray = myArray.length - 1;
i = 0;
while (lenArray < n) {
while (x < 3) {
var v = x + i;
outPut += myArray[v];
x++;
}
i++;
lenArray = myArray.length - 1;
myArray.push(outPut);
}
return myArray;
} else {
return [];
};
}
console.log(tribonacci([1, 1, 1], 10));
First, I tried to push first 3 items into "myArray".
Second, in the "while" loop, while it's less than "n" (number of items needed to be in the array), add the last 3 items in "myArray" until "myArray" reaches the needed "n" amount.
e.g. tribonacci([1,1,1],10)
Return should be [1,1,1,3,5,9,17,31,57,105]
Adding the last 3 items continuously until it reaches 10 items in an array.
Instead I get as result:
[1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3]
I have no idea why it's stuck on 3s.
I tried "i++" below the 2nd "while" loop so that it can start adding the last 3 items every time "myArray" grows by one, but that doesn't seem to be the issue. Is the "outPut" being stuck on 3?
var v = x + i; uses x to calculate the correct index.
After adding those 3 numbers, x needs to be reset.
Also, outPut is reused in the next iteration, but it keeps adding values, so your output will get to high. This needs to be reset to 0 after the 3-loop.
Add outPut = x = 0 after pushing outPut into myArray
function tribonacci(signature, n) {
var myArray = [];
var lenArray = 0;
var i = 0;
var outPut = 0;
var x = 0;
if (n > 0) {
while (i < 3) {
myArray.push(signature[i]);
i++;
}
lenArray = myArray.length - 1;
i = 0;
while (lenArray < n) {
while (x < 3) {
var v = x + i;
outPut += myArray[v];
x++;
}
i++;
lenArray = myArray.length - 1;
myArray.push(outPut);
outPut = x = 0
}
return myArray;
} else {
return [];
};
}
console.log(tribonacci([1, 1, 1], 10));
[
1,
1,
1,
3,
5,
9,
17,
31,
57,
105,
193,
355
]
Some bonus-tips to improve the readabilty:
Use (...) spread operator to replace the first while:
var myArray = [ ...signature ];
Use myArray.length - 1 instead off defining a variable with the same value
Use myArray[x + i] instead off defining another varible
Return on n < 0 to you don't need to intent that much
Applying those will give:
function tribonacci(signature, n) {
var myArray = [ ...signature ];
var i = 0;
var x = 0;
var outPut = 0;
if (n < 0) {
return [];
}
while ((myArray.length - 1) < n) {
while (x < 3) {
outPut += myArray[x + i];
x++;
}
i++;
myArray.push(outPut);
outPut = x = 0;
}
return myArray;
}
Hey there are you doing this for homework on while loops? If so refer to #0stone0 's answer which solves it right!
If on the other hand you want to dive into JS a little more, I'd suggest you a more coincise solution with some cool Array function:
function tribonacci(signature, size) {
const output = signature;
while(output.length < size) {
output.push(output.slice(-3).sum());
}
return output;
}
console.log({ result: tribonacci([1, 1, 1], 10) });
Some version of JS don't include the Array.sum function which would be something like this:
Array.prototype.sum = function() {
return this.reduce((sum, curr) => sum + curr, 0);
}
I am trying to debug the code below.
It is supposed to create a 2d-array, with all of the permutations of the input string.
It starts off great, and the initial string is pushed to the array, but after I run the reverse function in step 4, the value in strArr changes from having a length of 3 to a length of 2. basically like it is skipping the concat in the reverse function, but when I ran it in the debugger, z has a length of 3 after the concat, but then when the function returns it, the length becomes 2 again.
any help would be appreciated.
function permAlone(str) {
var perms = [];
var totalPerms = factorial(str.length);
var strCodes = converter(str);
var strArr = [];
strArr.push(strCodes);
// overall loop
for (var X = 0; X < totalPerms; X++) {
//step 1
var largestI = -1;
for (var i = 0; i < strCodes.length - 1; i++) {
if (strCodes[i] < strCodes[i + 1]) {
largestI = i;
}
}
//if none found break loop
if (largestI == -1) {
break;
}
//step 2
var largestJ = -1;
for (var j = 0; j < strCodes.length; j++) {
if (strCodes[largestI] < strCodes[j]) {
largestJ = j;
}
}
//step 3
swap(strCodes, largestI, largestJ);
//step 4
strCodes = reverse(strCodes, largestI);
//step 5 push to array
strArr.push(strCodes);
}
console.log(strArr);
return strArr;
}
function factorial(x) {
for (var i = x - 1; i > 0; i--) {
x *= i;
}
return x;
}
function converter(x) {
var temp = [];
for (var i = 0; i < x.length; i++) {
temp.push(x.charCodeAt(i));
}
return temp;
}
function swap(a, i, j) {
var temp = a[i];
a[i] = a[j];
a[j] = temp;
}
function reverse(z, a) {
var endArr = z.splice(a+1);
endArr.reverse();
z = z.concat(endArr);
return z;
}
debugger;
permAlone('abc');
The reverse function returns a new array and does not manipulate the existing. You need to change your code to the following:
endArr = endArr.reverse();
It looks like it was an issue with having a shallow copy of the array.
I added z = z.slice(); to the reverse function and it fixed the issue.
I have an array [1,2,4,5,1,7,8,9,2,3]
and i would like it to generate all subset which sum of values are less than 10
current result [[1,2,4],[5,1],[7],[8],[9],[2,3]]
expected result [[4,5,1],[9,1],[8,2],[3,7],[1,2]]
that is what i did
var a = [1,2,4,5,1,7,8,9,2,3], tempArr = []; tempSum = 0, result = [];
for (var i = 0;i< a.length; i += 1 ) {
tempSum+=a[i];
tempArr.push(a[i]);
if((tempSum+a[i+1])>10) {
result.push(tempArr);
tempSum = 0;
tempArr = [];
} else if (i == a.length-1 && tempArr.length > 0) { // if array is [1,2,3]
result.push(tempArr);
}
}
but it gives me [[1,2,4],[5,1],[7],[8],[9],[2,3]] and it has 6 subset, but i expect to get [[4,5,1],[9,1],[8,2],[3,7],[1,2]] which has 5 subset.
Below logic is in JavaScript :-
var limit = 10;
var arr = [1,2,4,5,1,7,8,9,2,3];
arr.sort();
var ans = new Array ( );
while(arr.length >0){
var ts = arr[arr.length-1];
arr.splice(arr.length-1 , 1);
var ta= new Array ( );
ta.push(ts);
var x = arr.length-1;
while(x>=0){
if(ts + arr[x] <= limit){
ts = ts + arr[x];
ta.push(arr[x]);
arr.splice(x , 1);
}
x= x-1;
}
ans.push(JSON.stringify(ta));
}
alert(ans);
It is Giving Output as required .
[9,1],[8,2],[7,3],[5,4,1],[2]
I have removed duplicates then added maxSum parameter to combine function to generate all subset which have those conditions and then sorted subsets by sum of the values and sliced them.
You could change parameters to fit it for your problem.
var arr = [1,2,4,5,1,7,8,9,2,3]
MAX_SUM = 10,
MIN_SUBSET_LEN = 2,
RESULT_LEN = 5;
//remove duplicates
var uniqeSet = arr.filter(function(value, index){
return this.indexOf(value) == index
},arr);
// a function to get all subset which
// their length are greater than minLength and
// sum of values are little than maxSum
var combine = function(sourceArr, minLength, maxSum) {
var fn = function(n, src, got, all, sum) {
if(sum <= maxSum){
if (n == 0) {
if (got.length > 0) {
all.push({arr:got,sum:sum});
}
return;
}
for (var j = 0; j < src.length; j++) {
var tempSum = sum
fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all, sum + src[j]);
}
}
return;
}
var all = [];
for (var i = minLength; i < sourceArr.length; i++) {
fn(i, sourceArr, [], all, 0);
}
return all;
}
var result = combine(uniqeSet, MIN_SUBSET_LEN, MAX_SUM);
var sortedSliced = result.sort(function(a1, a2){
return a2.sum - a1.sum;
}).slice(0, RESULT_LEN).map(function(m){return m.arr;});
console.log(JSON.stringify(sortedSliced));
I have this array:
var array = [0,1,2,3,4,5,6,7,8,9,10];
To Loop in a Clockwise Direction ( Start 8, then 9, then 10, then 0.....) I'm doing this way:
var start = 8;
for(i = 0; i < array.length; i++){
index = (start+i)%array.length;
....
}
1) To Clockwise Direction, there's a better way?
2) To Loop in a CounterClockwise Direction (Start 2, then 1, then 0, then 10...), what should I do?
To do it similar to what you did, decrease the index from the starting index and add the length before trimming:
var start = 8;
for(i = 0; i < array.length; i++) {
index = (start - i + array.length) % array.length;
// ....
}
Regarding "how to do it better", I'd create a simple helper function:
function getIndexInRange(index, length) {
var trim = index % length;
var nonNegative = trim + length;
return nonNegative % length;
}
Then it all becomes clearer:
var start = 8;
for(i = 0; i < array.length; i++) {
var index = getIndexInRange(start + i, array.length);
// ....
}
for(i = 0; i < array.length; i++) {
var index = getIndexInRange(start - i, array.length);
// ....
}
Now you can even iterate the array multiple times if you want, and it still works:
for(i = 0; i < array.length * 5; i++) {
var index = getIndexInRange(start - i, array.length);
// ....
}
var array = [0,1,2,3,4,5,6,7,8,9,10];
function clockwise(start){
for (var i = 0; i < array.length; i++){
console.log((start+i)%array.length);
}
}
function counterClockwise(start){
for (var i = array.length; i > 0; i--){
console.log((start+i)%array.length);
}
}
console.log('clockwise start from ');
clockwise(8);
console.log('clockwise End ');
console.log('counterClockwise start from ');
counterClockwise(2);
console.log('counterClockwise End ');
Consider using a single function for both directions:
var array = [0,1,2,3,4,5,6,7,8,9,10];
function iterateByClockRotation(start, array, direction){
var len = array.length, current = start;
while (len--) {
current = array.indexOf(current);
if (current < 0) current = ((direction === "clockwise")? 0 : array.length-1);
console.log(array[current]); // the current value
(direction === "clockwise")? current++ : current--;
}
}
iterateByClockRotation(8, array, "clockwise");
The output for 'clockwise' direction:
8
9
10
0
1
2
3
4
5
6
7
iterateByClockRotation(2, array, "anticlockwise");
The output for 'anticlockwise' direction:
2
1
0
10
9
8
7
6
5
4
3
I created a jsbin here.
http://jsbin.com/tucusal/edit?html,js,console
You can even create a function that takes direction input and then traverses array in that direction.
var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
var start = 8;
clockwise = 1;
anti_clockwise = -1;
direction = clockwise;
traverse(array, start, anti_clockwise);
function traverse(array, start, direction) {
var count = array.length;
for (i = start; count > 0; i += direction) {
var index = array[(array.length + i) % array.length];
count--;
console.log(index);
}
}
you can use Array.prototype.slice to change start of array:
Array.prototype.enhancedForEach = function(callback, start = 0, clockwise = true) {
var array = this;
start %= array.length;
array.slice(start)
.concat(array.slice(0, start))
.forEach((v, i, arr) => {
var index = clockwise ? i : arr.length - i - 1;
callback(arr[index], index, arr);
});
}
array = Array.from({
length: 20
}, (v, i) => i);
array.enhancedForEach(v => console.log(v), 4);
array.enhancedForEach(v => console.log(v), 0, false);
http://stackoverflow.com/posts/37981887/edit#