Strategy for creating N number nested loops in javascript - javascript

Suppose I have this pattern:
for(let i =0; i < 3; i++){
for(let j =0; j < 3; j++){
for(let k =0; k < 3; k++){
console.log(i,j,k)
}
}
}
Benefit is I have access to all iteration variables within innermost loop. i, j, and k, disadvantage is it is hardcoded to 3 levels of nested loops. If I want a fourth I need to add that code manually.
I am trying to generalize the code using recursion where I can set the number of nested loops to an arbitrary variable. Here is what I am trying:
const maxNestedLoops = 3;
const iterations = 3;
const indexes = [];
function looper(loopNumber){
for(indexes[loopNumber] = 0; indexes[loopNumber] < iterations; indexes[loopNumber]++){
if(loopNumber < maxNestedLoops){
looper(loopNumber + 1);
}
console.log(indexes);
}
}
looper(0);
The first results in the following in the console:
0, 0, 0
0, 0, 1
0, 0, 2
0, 1, 0
0, 1, 1
0, 1, 2
0, 2, 0
0, 2, 1
0, 2, 2
...and so on
However with my recursive function example it is not the same:
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 3, 3]
...and so on
Problems are that not only are there four indexes instead of three. But some of the values are 3s and I would expect it to only go up to 2.
Advice appreciated.

The problem is that:
your console.log should only be executed at the deepest level. So put that console.log in an else clause.
The base case happens when loopNumber === maxNestedLoops - 1 as that is the last index of your array, so the if condition should correspond to that
if (loopNumber < maxNestedLoops - 1){
looper(loopNumber + 1);
} else {
console.log(indexes);
}

There is problem with the for loop. indexes[loopNumber]++ will be increased until it will reach the value of iterations. Since your value of iterations is 3, you will end up with values which are equal to 3 in the indexes array, because the loop itself is modifying the array.
Also, since you modify your array if indexes before checking if the loopNumber reached the number of maxNestedLoops, you will end up with an array with length of maxNestedLoops + 1.
What I suggest you should do:
const maxNestedLoops = 3;
const iterations = 3;
const indexes = [];
function looper(loopNumber) {
// Check if we reached the number of nested loops before doing anything else.
if (loopNumber < maxNestedLoops) {
// Don't modify the indexes array directly, use a variable instead
for (let i = 0; i < iterations; i++) {
indexes[loopNumber] = i;
looper(loopNumber + 1);
console.log(indexes);
}
}
}
looper(0);

Here you go. It was a interesting one :)
const maxNestedLoops = 3;
const iterations = 3;
const indexes = [];
function looper(level){
for (let i=0; i<iterations; i++){
indexes.push(i);
if (level === maxNestedLoops-1) {
console.log(indexes);
indexes.splice(level,1);
continue;
}
looper(level + 1);
indexes.splice(level,1);
}
}
looper(0);

Related

For loop not iterating through whole array (Javascript)

I am trying to solve a problem that takes an array and moves all of the zeros to the end, preserving the order of the other elements.
This isn't iterating through the entire array, any ideas on what I'm doing wrong would be greatly appreciated.
let moveZeros = function (arr) {
let newArr = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) {
newArr.push(arr[i]);
arr.splice(i, 1);
}
}
return arr.concat(newArr);
};
moveZeros([9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]);
// expexted to return [9,9,1,2,1,1,3,1,9,9,0,0,0,0,0,0,0,0,0,0]
// currently returns [9,9,1,2,1,1,3,1,9,0,9,0,0,0,0,0,0,0,0,0]
// Strangely... sending a smaller array to the function seems to work.
//moveZeros([false,1,0,1,2,0,1,3,"a"])
// correctly returns[false,1,1,2,1,3,"a",0,0]
The problem isn't related to the length, it occurs when you have more than one consecutive 0. For example [0,0,9,0,0,9] will come out [0,9,0,9,0,0].
In the first iteration of the loop (i=0), you remove the first 0 from the array, leaving you with [0,9,0,0,9]. On the second iteration (i=1), it's now looking at the second element in the array, which is 9. The first 0 is skipped. This will happen later in the array too as the loop progresses.
In general it can be problematic to modify an array as you loop through it. There are a number of ways you could perform a sort like this. But to keep it close to your original you could do this:
let moveZeros = function(arr) {
let arrA = [];
let arrB = [];
for (let i = 0; i < arr.length; i++) {
if (arr[i] === 0) {
arrB.push(arr[i]);
} else {
arrA.push(arr[i]);
}
}
return arrA.concat(arrB);
};
Now the function keeps the original array intact as it goes through it. Non-0 items are pushed to arrA, and 0s are pushed to arrB, and then those two are concatenated.
You could keep the array and move all not zero values to the start and fill the rest with zeroes.
let moveZeros = function(array) {
let i = 0,
j = 0;
while (i < array.length) {
if (array[i] !== 0) array[j++] = array[i];
i++;
}
while (j < array.length) array[j++] = 0;
return array;
};
console.log(...moveZeros([9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
console.log(...moveZeros([false, 1, 0, 1, 2, 0, 1, 3, "a"]));

how to write a program to return bfs

I want to write a JavaScript function that takes 3 arguments: an adjacency matrix in the form of a 2D array, number of nodes, and starting vertex.
The function returns the BFS traversal of the graph represented by the adjacency matrix.
function task24(mat,n,v){
let visited=new Array(n)
let queue=[]
let result=[]
while(queue.length!=0){
queue.push(v)
result.push(queue.pop())
for(i=0;i<n;i++){
visited[i]=0
}
let i=v
visited[i]=1
for(j = 0; j < n; j++) {
if(visited[j] == 0 && Adj[i][j] == 1) {
visited[j] = 1;
queue.push(j)
result.push(j)
i=queue.shift()
}
}
}
return result
}
console.log(task24([[0, 1, 0, 0], [0, 1, 1, 1], [1, 0, 0, 1], [0, 0, 1, 0]],4,2))```
Please see comments in code snippet for details. Also you can use a map to replace your visited array, probably it is more efficient if there are some nodes not connected to any other nodes.
function task24(mat,n,v){
const visited=[], queue=[v], result = [];
//start from v and v is already in queue, so visited[v] is 1
visited[v] = 1;
//loop until queue is empty
while(queue.length>0){
//remove the node index from queue using shift and push it result
const curr = queue.shift();
result.push(curr);
//check connected nodes
for(let i = 0; i<mat[curr].length; i++){
//if a node is connected and has not been seen, mark it as seen and push it to queue
if(mat[curr][i] === 1 && !visited[i]){
visited[i] = 1;
queue.push(i)
}
}
}
return result
}
console.log(task24([[0, 1, 0, 0], [0, 1, 1, 1], [1, 0, 0, 1], [0, 0, 1, 0]],4,2))

Move specified characters in an array

I get a list of elements given as parameter, this is input.
JSON.stringify(moveZeros([1,2,0,1,0,1,0,3,0,1]))
Script should move zeros to the end without changing other elements order.
My solution would looks like this code:
var moveZeros = function (arr) {
var args = Array.prototype.slice.call(arguments);
for(var i=0; i<args.length; i++) {
if (args[i] == 0)
args.splice(i);
args.push(0);
}
console.log(args);
return args;
}
This should append element's with zero value and append a new, 0 value to end of array. It only prints original array without modification.
It is because arguments is all the arguments passed in. You should be using arr and not arguments.
var moveZeros = function (arr) {
var args = arr;
...
}
Also another issue you will face is when you loop from the start to the end and you move elements to the end, you will be skipping indexes as stuff slides down to fill in the hole that you just created. You should be using reduce() or loop from the end to the start if you are using a for loop.
var moveZeros = function(arr) {
var args = arr;
for (var i = args.length - 1; i >= 0; i--) {
if (args[i] === 0) {
args.splice(i, 1);
args.push(0);
}
}
return args;
}
console.log(JSON.stringify(moveZeros([1, 2, 0, 1, 0, 1, 0, 3, 0, 1])));
You could use the copy of arr and use a variable for the length check. If a zero is found, the length variable is decremented and the zero is pushed to the end. If not found, the index is incremented.
var moveZeros = function (arr) {
var args = arr.slice(),
i = 0,
l = args.length;
while (i < l) {
if (args[i] === 0) {
args.push(args.splice(i, 1)[0]);
l--;
} else {
i++;
}
}
return args;
}
console.log(moveZeros([1, 2, 0, 1, 0, 1, 0, 3, 0, 1]));
Or use a loop from the end.
var moveZeros = function (arr) {
var args = arr.slice(),
i = args.length;
while (i--) {
if (args[i] === 0) {
args.push(args.splice(i, 1)[0]);
}
}
return args;
}
console.log(moveZeros([1, 2, 0, 1, 0, 1, 0, 3, 0, 1]));
You had some brackets wrong and missed some arguments. Also you are using an array as the parameter, no need to slice the arguments. You also missed the number of elements for the splice
This does not work for something like [9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0] (see update further below)
// DOES NOT WORK
var moveZeros = function (arr) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] == 0) {
arr.splice(i, 1);
arr.push(0);
}
}
console.log(arr);
return arr;
}
Working solution
var moveZeros = function(args) {
var r = [];
var zeros = 0;
for (var i = 0; i < args.length; i++) {
if (args[i] !== 0) {
r.push(args[i]);
} else zeros++
}
r = r.concat(Array(zeros).fill(0))
console.log(r);
return args;
}
JSON.stringify(moveZeros([9, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
You have to provide count of values to be removed in splice() method.
Also push 0 only if the value was 0. You are pushing it everytime. (Hint: Because the push statement is not in if).
With a copy of array :
You can maintain a new array and push only non zzero values on to it and later push all the zeros. Simple to understand.
var moveZeros = function(args) {
var arr = [], zCount = 0;
for (var i = 0; i < args.length; i++) {
args[i] == 0 ? zCount++ : arr.push(args[i]);
}
while (zCount-- > 0) arr.push(0);
console.log(arr);
return arr;
}
JSON.stringify(moveZeros([9, 0, 0, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
With reverse loop :
You need a reverse loop because every time when you splice you skip an index so repeated 0s will not be removed.
var moveZeros = function(args) {
for (var i = args.length; i > 0; i--) {
if (args[i] == 0) {
args.splice(i, 1);
args.push(0);
}
}
console.log(args);
return args;
}
JSON.stringify(moveZeros([9, 0, 0, 0, 9, 1, 2, 1, 1, 3, 1, 9, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0]));
I think this would be the funniest way and nicest as line of code and as it's using built-in function (sort) then it faces less performance issues.
var array = [1, 2, 0, -1, 0, 1, 0, 3, 0, 1]
array.sort(a => a === 0);
console.log(array);
first its bad to modify an array while you iterate over it. this can cause some weird jumping that will be confusing.
say you have 1,0,0,1
in your loop when you get to index one it will splice and append the zero to the end of the array
1,0,1,0
but the counter will then increment to 2 and it will miss the 0 which is now at position 1.
It looks like youre trying to create a shallow copy with
var args = Array.prototype.slice.call(arguments);
but I am not really sure.
I wouldn't modify the array you want to return but rather create some temp array and loop through the input, if its 0 up some counter, if its not 0 push it to the holding array, then push on as many 0s as the counter tells you to and return the temp array

Javascript - Creating pyramid array from number values

I am looking to create a pyramid array from number values. The requirement is that I am to work from an array that which in my case has 3 rows with 5 columns. I need to create a pyramid that looks like the example below.
Here is what I want to achieve exactly..
[0, 0, 1, 0, 0]
[0, 1, 1, 1, 0]
[1, 1, 1, 1, 1]
So far my code looks something like this..
var a = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]];
function array_pyramid( array ){
for (i = 0; i < array.length; i++){
for (j = 0; j <= Math.floor(array[i].length / 2); j++){
var tmp = array[i].length / 2;
console.log (Math.floor(tmp));
if ( i < j ) {
array[i][j] = 1;
}
}
}
return array;
}
function print_array(array) {
for (var i = 0; i < array.length; i++) {
console.log(array[i]);
}
}
//console.log(a);
//print_array(a);
print_array(array_pyramid(a));
Try example below. I purposely kept variable names long and descriptive and kept operations in multiple steps rather than inline for the ease of analyzing.
var a = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]];
function array_pyramid(array){
var pBlocksCount = 1;
var currentRow, rowIndex, columnIndex, columnsCount, rowsCount = array.length;
for(rowIndex=0; rowIndex<rowsCount; rowIndex++){
currentRow = array[rowIndex];
columnsCount = currentRow.length;
var piramidDrawIndex = parseInt(columnsCount/2);
var piramidDrawOffset = parseInt(pBlocksCount/2);
var piramidDrawTrigger = piramidDrawIndex-piramidDrawOffset;
var blocksToDraw = pBlocksCount;
for(columnIndex=0; columnIndex<columnsCount; columnIndex++){
if(columnIndex>=piramidDrawTrigger){
if(blocksToDraw){
currentRow[columnIndex]=1;
blocksToDraw--;
}
}
}
pBlocksCount+=2;
}
return array;
}
function print_array(array) {
for (var i = 0; i < array.length; i++) {
console.log(array[i]);
}
}
print_array(array_pyramid(a));
This will probably fail for most other cases you could come up with, but works in this specific instance:
function array_pyramid( array ){
var mid = Math.floor(array[0].length/2);
for(var i=0;i<array.length;i++){
for(var j=0;j<array[i].length;j++){
var boundsLower = mid-i;
var boundsUpper = mid+i;
if(j>=boundsLower && j<=boundsUpper){
array[i][j] = 1;
}
}
}
return array;
}
Live example: https://jsfiddle.net/o6t1cbdu/
Try this ...
function array_pyramid(array){
var limit = [0,0];
for (var i = array.length - 1, limit = [0, (array[0] || []).length - 1]; i >= 0; i--) {
for (var j = limit[0]; j <= limit[1]; j++) {
array[i][j] = 1;
}
limit[0]++; limit[1]--;
}
return array;
}
I hope it helps
You were starting down the right track with an inner and an outer loop, as well as your use of division to try to account for the midpoint.
Here is a verbose and heavily commented modification of your code that accounts for any number of columns (odd or even) and uses a range that widens during row progression to determine which columns should be 1 or 0. I've included a second array, b, to show it works for even column counts as well.
var a = [
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0]
],
b = [
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0]
];
function array_pyramid (a) {
// Use first row to determine row length, assume all others are the same
var base_length = a[0].length,
// Get a modulus to account for odd or even row length
mod = base_length % 2,
// Find the middle point
mid_point = Math.floor(base_length / 2) + mod,
// Account for zero-index of arrays
zero_indexed = mid_point - 1,
// Produce an initial set of boundaries
marker_range = {
lower: zero_indexed,
upper: (mod === 1) ? zero_indexed : (zero_indexed + 1)
};
// Iterate the array's rows
a.forEach(function (row) {
// Iterate the row's cols
row.forEach(function (col, i) {
// Compare index to boundaries to determine if we're outputting a 1 or a 0
row[i] = (i >= marker_range.lower && i <= marker_range.upper) ? 1 : 0;
});
// If we still have room to expand, widen the range in each direction
if (marker_range.lower > 0) {
marker_range.lower -= 1;
marker_range.upper += 1;
}
});
return a;
}
function print_array(a) {
var i;
console.log('-- Start output');
for (i = 0; i < a.length; i++) {
console.log(' ', a[i]);
}
console.log('-- End output');
console.log(' ');
}
console.log('a (odd col count):');
print_array(array_pyramid(a));
console.log('b: (even col count)');
print_array(array_pyramid(b));
So here is my code, it works fine.
The idea is that you find the main vertical center of the matrix and the sideA and sideB variables represent the left and the right side of the pyramid. Once you've found the sides, you make them equal to one as well as everything between them ( if (j <= sideA && j >= sideB) ). I hope that helps. :)
function array_pyramid ( array ) {
var item, sideA, sideB;
if (array instanceof Array) {
for (var i = 0; i < array.length; i++) {
item = array[i];
centerIndex = Math.floor(item.length / 2);
sideA = centerIndex + i;
sideB = centerIndex - i;
for (var j = 0; j < item.length; j++) {
if (j <= sideA && j >= sideB) {
item[j] = 1;
}
}
}
return array;
} else {
return 'Invalid array';
}
}
array_pyramid ([
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
]);
When you run that code in the console it will return this:
[0, 0, 1, 0, 0]
[0, 1, 1, 1, 0]
[1, 1, 1, 1, 1]
And that's it.

Loop to modify a variable each iteration, and append each new "modification" to an array

I'm trying to generate a card deck for the game SET, or, for those who don't know what that is, I'm trying to fill an array with unique elements of the form [a, b, c, d], where 0 <= a, b, c, d <= 2 (81 elements). So, I want [[0, 0, 0, 0,], [0, 0, 0, 1], ... , [2, 2, 2, 2]] (the order doesn't matter). This is the code I have so far:
var deck = [],
count = [0, 0, 0, 0];
for (var i = 0; i < 81; i++) { // go through all combos
deck.push(count); // append the current modification of count to deck
// increment count by 1, carrying "tens" if necessary
for (var j = 3; count[j] === 2; j--) {
// if the "digit" is 2, make it 0 since it overflows
count[j] = 0;
}
// j is the first "digit" of count that isn't already 2, so we add 1 to it
count[j]++;
}
Instead, what this seems to do is fill the deck array with the last modification of count; if the upper bound on i is 81, this is [0, 0, 0, 0] since it rolls over all the way around, and if you change the bound to anything lower it will respond accordingly. Why does this happen? What mistake have I made?
Just remember that you're pushing the same count into deck each iteration, which means any alternation to count will reflect to all other counts in deck, as they're all reference to same array
You can use .slice to clone a new array with same value from previous count.
var deck = [],
count = [0, 0, 0, 0];
for (var i = 0; i < 81; i++) { // go through all combos
// Clone a new array from previous one.
count = count.slice();
deck.push(count); // append the current modification of count to deck
// increment count by 1, carrying "tens" if necessary
for (var j = 3; count[j] === 2; j--) {
// if the "digit" is 2, make it 0 since it overflows
count[j] = 0;
}
// j is the first "digit" of count that isn't already 2, so we add 1 to it
count[j]++;
}
console.log(deck);

Categories