I have struggled to understand the output of this nested loop for a long time. I really want to understand what it does.
I would expect it to output: [ [ 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ]
But the actual output is: [ [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0 ] ]
After the first inner loop row contains two 0's and should be pushed to the newArray in the outer loop. It looks like this isn't happenening and I can't figure out why. The first element should be [0, 0], right?.
I really hope someone can see what I mean here and explain what's happening! Thank you!
function zeroArray(m, n) {
// Creates a 2-D array with m rows and n columns of zeroes
let newArray = [];
let row = [];
for (let i = 0; i < m; i++) {
// Adds the m-th row into newArray
for (let j = 0; j < n; j++) {
// Pushes n zeroes into the current row to create the columns
row.push(0);
}
// Pushes the current row, which now has n zeroes in it, to the array
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
You're passing row to all the iterations of the loop. To achieve what you want, row must be unique through each iteration of the loop, so you need to move it inside the first loop.
To better understand the issue, read more about values and by references in JavaScript: https://www.javascripttutorial.net/javascript-pass-by-value/#:~:text=JavaScript%20pass%2Dby%2Dvalue%20or%20pass%2Dby%2Dreference&text=It%20means%20that%20JavaScript%20copies,variables%20outside%20of%20the%20function.
function zeroArray(m, n) {
// Creates a 2-D array with m rows and n columns of zeroes
let newArray = [];
for (let i = 0; i < m; i++) {
let row = [];
// Adds the m-th row into newArray
for (let j = 0; j < n; j++) {
// Pushes n zeroes into the current row to create the columns
row.push(0);
}
// Pushes the current row, which now has n zeroes in it, to the array
newArray.push(row);
// Update n to get desired pattern
n += 2
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
That's because JavaScript objects (and arrays), are just a reference in memory. so you are creating a single array of arrays that share the same address in memory, because they share the same address, when you update it (Array.prototype.push), you are updating all of them. The solution is to create a new row in the first loop:
function zeroArray(m, n) {
const newArray = [];
for (let i = 0; i < m; i++) {
// If this isn't the first run, take the last value of row
const prevRow = i > 0 ? newArray[i-1] : [];
const row = [...prevRow]; // By using the spread operator(...), you can copy an array
for (let j = 0; j < n; j++) {
// Pushes n zeroes into the current row to create the columns
row.push(0);
}
// Pushes the current row, which now has n zeroes in it, to the array
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
Important note
This isn't a natural way of writing JavaScript, you can achive the same with:
const zeroArray = (m, n) => Array.from({ length : m }).map((value, index) => {
return Array.from({ length : (index + 1) * n }).map(() => 0);
});
Related
I'm doing a learning exercise and am trying to understand the following code. I thought I had a handle on arrays and loops, but this one has got me very confused.
The below code:
function zeroArray(m, n)
{
let newArray = [];
let row = [];
for (let i = 0; i < m; i++)
{
for (let j = 0; j < n; j++)
{
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
Returns
[ [ 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0 ],
[ 0, 0, 0, 0, 0, 0 ] ]
However I would have expected it to return
[ [ 0, 0, ],
[ 0, 0, 0, 0, ],
[ 0, 0, 0, 0, 0, 0 ] ]
Given that in each i loop, we are pushing (0) to row[] twice, before pushing row[] into newArray.
This isn't happening though, and in my VSCode debugger it looks as though in each i loop, every existing index of newArray is being updated with the latest version of the row[] array.
Why is this?
1) Start outer loop with i = 1 upto i <= m, so the loop count will be m
for (let i = 1; i <= m; i++) {
2) You should create a new row every time the inner loop start and push row into newArray after the inner loop ends
3) Set inner loop condition as j < n * i
for (let j = 0; j < n * i; j++) {
function zeroArray(m, n) {
let newArray = [];
// const row = [] // (-)
for (let i = 1; i <= m; i++) {
const row = []; // (+)
for (let j = 0; j < n * i; j++) { // (+)
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
/* This is not a part of answer. It is just to give the output fill height. So IGNORE IT */
.as-console-wrapper { max-height: 100% !important; top: 0; }
You need to make a copy of the array when pushing to newArray:
function zeroArray(m, n) {
let newArray = [];
let row = [];
for (let i = 0; i < m; i++) {
for (let j = 0; j < n; j++) {
row.push(0);
}
newArray.push(row.slice());
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(JSON.stringify(matrix));
Matrix m x n should be m rows and n cols.
So for 3, 2 you expect
[
[0, 0],
[0, 0],
[0, 0],
]
Just declare row inside the first loop:
function zeroArray(m, n) {
const newArray = [];
for (let i = 0; i < m; i++) {
const row = [];
for (let j = 0; j < n; j++) {
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
console.log(matrix);
This question already has answers here:
Copy array by value
(39 answers)
Closed 3 years ago.
My code:
function zeroArray() {
let newArray = [];
let row = [];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 2; j++) {
row.push(0);
}
newArray.push(row);
}
return newArray
}
console.log(zeroArray())
From my perspective, its look like the result will be :
[[ 0, 0 ],[ 0, 0, 0, 0 ],[ 0, 0, 0, 0, 0, 0 ]]
but when the code run in the console it shows this, why is that?
[ [ 0, 0, 0, 0, 0, 0 ],[ 0, 0, 0, 0, 0, 0 ],[ 0, 0, 0, 0, 0, 0 ] ]
function zeroArray() {
let newArray = [];
let row = [];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 2; j++) {
row.push(0);
}
newArray.push(row); // <== Problematic line
}
return newArray;
}
The problem is when you push row array into newArray, it doesn't actually push the values into it; it pushes the pointer to that array, which means after updating row array, if you push it again, it will push the same value to newArray.
Posted the illustration for detailed explanation:
The solution is as some other people noted, re-creating the array each time instead of using the same variable. For example, the solution could be:
function zeroArray() {
const newArray = [];
for (let i = 0; i < 3; i++) {
const row = [];
for (let j = 0; j < 2; j++) {
row.push(0);
}
newArray.push(row); // <== Problematic line
}
return newArray;
}
Looks like an unexpected instance of mutating an object. In this case, you're pushing the same row multiple times, then updating it (resulting in that it updates the three instances of it that end up in newArray). If you instead use something like newArray.push(row.slice()), the output will be as you expect.
I'm writing a simple minesweeper game in Javascript. I have a 2-dimensional array (called "mineInput") to hold the locations of all the mines. And I have a separate array "result" which records the number of mines adjacent to each cell.
I have 2 nested for loops to iterate over each row and each column in "result" and check every sell in 'mineInput". If there is a mine, I increment the mine count with result[i][j]++;. However I noticed weird behavior where the entire column is incremented instead of just one cell.
this:
[ [ 0, 0, 0 ],
[ 0, 0, 0 ],
[ 0, 0, 0 ] ]
followed by: result[i][j]++;
becomes:
[ [ 0, 1, 0 ],
[ 0, 1, 0 ],
[ 0, 1, 0 ] ]
instead of:
[ [ 0, 0, 0 ],
[ 0, 1, 0 ],
[ 0, 0, 0 ] ]
Here is the full code (pls excuse the obscene number of console logs).
https://repl.it/BIYH/2
Any idea what's wrong?
The issue is early in your code where you initialise your arrays
var result = [];
var newRow = [];
for (var i = 0; i < rowCount; i++) {
newRow.push(0);
}
for (var i = 0; i < rowCount; i++) {
result.push(newRow);
}
You have created only one newRow array and added it to your result array 3 times. So you could show this like:
newRow == [0,0,0]
result == [newRow, newRow, newRow]
When you increment you add to one cell in the newRow array which gives
newRow == [0,1,0]
result == [newRow, newRow, newRow] therefore
result = [[0,1,0],[0,1,0],[0,1,0]]
you can fix this like this:
var result = [];
for (var i = 0; i < rowCount; i++) {
// create a new array for each row
var newRow = [];
for (var i = 0; i < rowCount; i++) {
newRow.push(0);
}
result.push(newRow);
}
I guess the problem is in the following code:
var result = [];
var newRow = [];
for (var i = 0; i < rowCount; i++) {
newRow.push(0);
}
for (var i = 0; i < rowCount; i++) {
result.push(newRow);
}
You add the same array over and over again to the array result. Instead, you want to create a new array for each row:
var result = [];
for (var i = 0; i < rowCount; i++) {
var newRow = [];
for (var j = 0; j < rowCount; j++) {
newRow.push(0);
}
result.push(newRow);
}
An array is an object in javascript, and objects are always passed by reference.
Why won't this function reverseArrayInPlace work? I want to do simply what the function says - reverse the order of elements so that the results end up in the same array arr. I am choosing to do this by using two arrays in the function. So far it just returns the elements back in order...
var arr = ["a","b","c","d","e","f"]
var arr2 = []
var reverseArrayInPlace = function(array){
var arrLength = array.length
for (i = 0; i < arrLength; i++) {
arr2.push(array.pop())
array.push(arr2.shift())
}
}
reverseArrayInPlace(arr)
Here's a simpler way of reversing an array, using an in-place algorithm
function reverse (array) {
var i = 0,
n = array.length,
middle = Math.floor(n / 2),
temp = null;
for (; i < middle; i += 1) {
temp = array[i];
array[i] = array[n - 1 - i];
array[n - 1 - i] = temp;
}
}
You "split" the array in half. Well, not really, you just iterate over the first half. Then, you find the index which is symmetric to the current index relative to the middle, using the formula n - 1 - i, where i is the current index. Then you swap the elements using a temp variable.
The formula is correct, because it will swap:
0 <-> n - 1
1 <-> n - 2
and so on. If the number of elements is odd, the middle position will not be affected.
pop() will remove the last element of the array, and push() will append an item to the end of the array. So you're repeatedly popping and pushing just the last element of the array.
Rather than using push, you can use splice, which lets you insert an item at a specific position in an array:
var reverseArrayInPlace = function (array) {
var arrLength = array.length;
for (i = 0; i < arrLength; i++) {
array.splice(i, 0, array.pop());
}
}
(Note that you don't need the intermediate array to do this. Using an intermediate array isn't actually an in-place reverse. Just pop and insert at the current index.)
Also, interesting comment -- you can skip the last iteration since the first element will always end up in the last position after length - 1 iterations. So you can iterate up to arrLength - 1 times safely.
I'd also like to add that Javascript has a built in reverse() method on arrays. So ["a", "b", "c"].reverse() will yield ["c", "b", "a"].
A truly in-place algorithm will perform a swap up to the middle of the array with the corresponding element on the other side:
var reverseArrayInPlace = function (array) {
var arrLength = array.length;
for (var i = 0; i < arrLength/2; i++) {
var temp = array[i];
array[i] = array[arrLength - 1 - i];
array[arrLength - 1 - i] = temp;
}
}
If you are doing Eloquent Javascript, the exercise clearly states to not use a new array for temporary value storage. The clues in the back of the book present the structure of the solution, which are like Stefan Baiu's answer.
My answer posted here uses less lines than Stefan's since I think it's redundant to store values like array.length in variables inside a function. It also makes it easier to read for us beginners.
function reverseArrayInPlace(array) {
for (var z = 0; z < Math.floor(array.length / 2); z++) {
var temp = array[z];
array[z] = array[array.length-1-z];
array[array.length-1-z] = temp;
}
return array;
}
You are calling the function with arr as parameter, so both arr and array refer to the same array inside the function. That means that the code does the same as:
var arr = ["a","b","c","d","e","f"]
var arr2 = []
var arrLength = arr.length;
for (i = 0; i < arrLength; i++) {
arr2.push(arr.pop())
arr.push(arr2.shift())
}
The first statements get the last item from arr and places it last in arr2. Now you have:
arr = ["a","b","c","d","e"]
arr2 = ["f"]
The second statement gets the first (and only) item from arr2 and puts it last in arr:
arr = ["a","b","c","d","e","f"]
arr2 = []
Now you are back where you started, and the same thing happens for all iterations in the loop. The end result is that nothing has changed.
To use pop and push to place the items reversed in the other array, you can simply move the items until the array is empty:
while (arr.length > 0) {
arr2.push(arr.pop());
}
If you want to move them back (instead of just using the new array), you use shift to get items from the beginning of arr2 and push to put them at the end of arr:
while (arr2.length > 0) {
arr.push(arr2.shift());
}
Doing a reversal in place is not normally done using stack/queue operations, you just swap the items from the beginning with the items from the end. This is a lot faster, and you don't need another array as a buffer:
for (var i = 0, j = arr.length - 1; i < j; i++, j--) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
This swaps the pairs like this:
["a","b","c","d","e"]
| | | |
| +-------+ |
+---------------+
I think you want a simple way to reverse an array. Hope it will help you
var yourArray = ["first", "second", "third", "...", "etc"]
var reverseArray = yourArray.slice().reverse()
console.log(reverseArray)
You will get
["etc", "...", "third", "second", "first"]
With the constraints I had for this assignment, this is the way I figured out how to solve the problem:
var arr = ["a","b","c","d","e","f"]
var arr2 = []
var reverseArrayInPlace = function(array){
var arrLength = array.length
for (i = 0; i < arrLength; i++) {
arr2.push(array.pop())
}
for (i = 0; i < arrLength; i++) {
array[i] = arr2.shift()
}
}
reverseArrayInPlace(arr)
Thank you for all your help!
***** edit ******
For all of you still interested, I rewrote it using some help from this thread and from my own mental devices... which are limited at this point. Here is it:
arr = [1,2,3,4,5,6,7,8,9,10,11,12,13]
arr2 = ["a","b","c","d","e","f"]
arr3 = [1,2,3]
arr4 = [1,2,3,4]
arr5 = [1,2,3,4,5]
var reverseArrayInPlace2 = function(array) {
var arrLength = array.length
var n = arrLength - 1
var i = 0
var middleTop = Math.ceil(arrLength/2)
var middleBottom = Math.floor(arrLength/2)
while (i < Math.floor(arrLength/2)) {
array[-1] = array[i]
array[i] = array[n]
array[n] = array[-1]
// console.log(array)
i++
n--
}
return array
}
console.log(reverseArrayInPlace2(arr))
console.log(reverseArrayInPlace2(arr2))
console.log(reverseArrayInPlace2(arr3))
console.log(reverseArrayInPlace2(arr4))
console.log(reverseArrayInPlace2(arr5))
P.S. what is wrong with changing global variables? What would the alternative be?
Here is my solution with no temp array. Nothing groundbreaking, just shorter version of some proposed solutions.
let array = [1, 2, 3, 4, 5];
for(let i = 0; i<Math.floor((array.length)/2); i++){
var pointer = array[i];
array[i] = array[ (array.length-1) - i];
array[(array.length-1) - i] = pointer;
}
console.log(array);
//[ 5, 4, 3, 2, 1 ]
I know this is a old question, but I came up with an answer I do not see above. It is similar to the approved answer above, but I use array destructuring instead of a temporary variable to swap the elements in the array.
const reverseArrayInPlace = array => {
for (let i = 0; i < array.length / 2; i++) {
[array[i], array[array.length - 1 - i]] = [array[array.length - 1 - i], array[i]]
}
return array
}
const myArray = [1,2,3,4,5,6,7,8,9];
console.log(reverseArrayInPlace(myArray))
This solution uses a shorthand for the while
var arr = ["a","b","c","d","e","f"]
const reverseInPlace = (array) => {
let end = array.length;
while(end--)
array.unshift(array.pop());
return array;
}
reverseInPlace(arr)
function reverseArrayInPlace (arr) {
var tempArr = [];
for (var i = 0; i < arr.length; i++) {
// Temporarily store last element of original array
var holdingPot = arr.pop();
// Add last element into tempArr from the back
tempArr.push(holdingPot);
// Add back value popped off from the front
// to keep the same arr.length
// which ensures we loop thru original arr length
arr.unshift(holdingPot);
}
// Assign arr with tempArr value which is the reversed
// array of the original array
arr = tempArr;
return arr;
}
Having some trouble with this script. It iterates through a two dimensional array and adds each corresponding index together. So basically arr[0][1] + arr[0][2] + arr[0][3] ... arr[1][1] + arr[1][2] + arr[1][3] ...etc.
This first one works fine. So my logic is ok. My problem here is that I can't create the indices dynamically. I don't think a push will work since I'm summing values here.
var cat_stats_week_radar = [[0,0,0,0,0,0,0,0,0,0,0,0,0,0]];
for (var i = 0; i < cat_stats_week.length; i++) {
for (var j = 0; j < cat_stats_week[0].length; j++) {
cat_stats_week_radar[0][j] += +(cat_stats_week[i][j]);
}
}
This one doesn't work, I don't get an error, just a bunch of NaN values.
var cat_stats_week_radar = [[]];
for (var i = 0; i < cat_stats_week.length; i++) {
for (var j = 0; j < cat_stats_week[0].length; j++) {
cat_stats_week_radar[0][j] += +(cat_stats_week[i][j]);
}
}
Here are the arrays I'm working with.
Array to add:
var cat_stats_week = [
[0,0,0,0,0,0,0,1,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0],
[0,0,0,0,0,0,1,0,0,0,0,0,0,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,0],
[0,0,0,0,0,0,0,0,0,0,0,0,1,0],
[0,0,1,0,0,0,0,0,0,0,0,0,0,0]
];
Resulting array:
var cat_stats_week_radar = [[0, 0, 1, 0, 0, 0, 2, 1, 0, 0, 0, 0, 2, 0]];
You need to initialize it with the right number of zeroes:
var cat_stats_week_radar = [[]];
for (var i = 0; i < cat_stats_week[0].length; i++) {
cat_stats_week_radar[0].push(0);
}
And with Underscore.js:
_.map(_.zip.apply(null, cat_stats_week), function(a) {
return _.reduce(a, function(a, b) {
return a + b
})
});