How nested loops are executed in this case? - javascript

Why the result is [[0,0,0,0,0,0],[0,0,0,0,0,0],[0,0,0,0,0,0]] ?
How is this code executed? Cause is not so intuitive..
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);

Your code adds to row m * n times. The row is pushed m times into new Array
The fact that you add to row after you push it into newArray, doesn't mean row is magically cleared
To prove it is the same array in each row, consider this 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);
matrix[0][0]=1;
console.log(JSON.stringify(matrix));
See how changing [0][0] also changes [1][0] and [2][0] - that's because each row is the exact same array
Note: In the solutions below, I've added matrix[0][0] = 1 to demonstrate that the above problem is not present, i.e. each row is a different array - obviously you'd not include that line in your actual code
Two simple solutions: 1 - set row = [] after you push it
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);
row = [];
}
return newArray;
}
let matrix = zeroArray(3, 2);
matrix[0][0]=1;
console.log(JSON.stringify(matrix));
solution 2: declare row inside the first loop
function zeroArray(m, n) {
let newArray = [];
for (let i = 0; i < m; i++) {
let row = [];
for (let j = 0; j < n; j++) {
row.push(0);
}
newArray.push(row);
}
return newArray;
}
let matrix = zeroArray(3, 2);
matrix[0][0]=1;
console.log(JSON.stringify(matrix));
and a modern solution
function zeroArray(m, n) {
return Array.from({length: m}).map(() => Array(n).fill(0));
}
let matrix = zeroArray(3, 2);
matrix[0][0]=1;
console.log(JSON.stringify(matrix));

Related

Generate new random values and store in an array

I am trying to generate two random values and store them in an array however I would like them to be different values each time a random number between 0-3 is generated.
function new_Randomvalues(n) {
var array1 = [];
for (var i = 0; i < n; i++) {
var row = Math.round(3*Math.random());
var col = Math.round(3*Math.random());
array1.push([row,col]);
}
return array1;
}
console.log(new_Randomvalues(10));
How do I edit this function where if an array of same numbers are pushed into array1 then remove those and generate 10 unique coordinates.
Help would be appreciated thanks
Per sam's question in the comments, here's the code:
function shuffle(array) {
for (var i = array.length - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
pairs = [];
for (let i = 0; i <= 3; i++) {
for (let j = 0; j <= 3; j++) {
pairs.push([i, j]);
}
}
shuffle(pairs);
pairs = pairs.slice(0, 10);
console.log(pairs);

JavaScript updating 2d array value

I'm trying to update every element of a 2d array only once. But unexpectedly array item gets update multiple times.
For example:
const s = "ab";
const m = [...Array(s.length).fill([...Array(s.length).fill("")])]
for(let row = 0; row < s.length; row++) {
for (let col = 0; col < s.length; col++) {
console.log(row, col)
m[row][col] += `<${row}${col}>`
}
}
console.log(m)
it should return m = [ [ '<00>', '<01>' ], [ '<10>', '<11>' ] ]
but it returns m = [ [ '<00><10>', '<01><11>' ], [ '<00><10>', '<01><11>' ] ] instead.
Can anyone explain it, please?
Update:
Here I'm looping through each item once, so there should be no
chance of updating the item twice ( two value should not concat here )
Instead of initializing array with specific length. You may just initialize empty array then push the elements. Because if you do + of 2 strings, it may just concatenate it, for example:
console.log("I "+"am");
Full working code:
const s = "ab";
let m = [];
for(let row = 0; row < s.length; row++) {
m.push([]);
for (let col = 0; col < s.length; col++) {
m[row].push(`<${row}${col}>`);
}
}
console.log(m);
The issue is that you are using <${row}${col}>, You should simple use let size=0 and use Array[i][j] = size++; in place of <${row}${col}>
you should use the following way for 2D Arrays
//Create a new 1D array
let myArray = new Array(2);
// Loop to create 2D array using 1D array
for (let i = 0; i < myArray.length; i++) {
myArray[i] = new Array(2);
}
//declare size
let size = 0;
// Loop to initialize 2D array elements.
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
myArray[i][j] = size++;
}
}
// Loop to display the elements of 2D array.
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
document.write(myArray[i][j] + " ");
}
document.write("<br>");
}
You don't even need a loop, you can use map instead.
const a = Array(2).fill(null).map(
(_, row) => Array(2).fill(null).map(
(_, col) => `<${row}${col}>`
)
);

Why does function reassign 2Darray argument as sideeffect from calling the latter

When I run the third line alone and log test2darr it returns a 2D array filled with 6's in a 3x3 matrix
But when I run the fourth line and log test2darr again, it returns:
[4, 5, 4]
​
[5, 6, 5]​
[4, 5, 4]
(as well as for secondtest)
Though it should return the same array of 6's for test2darr and on assign the 2d array to secondtest
const n = 3;
const filler = new Array(n * n);
const test2darr = fill2DarrFromArr(filler.fill(6));
const secondtest = pileReduce(test2darr);
Here is my code for fill2DarrFromArr and pileReduce:
function pileReduce(_cells) {
_cells = fillEmpty(_cells);
for (let j = 0; j < _cells.length; j++) { //The Algorithm itself is not important
for (let i = 0; i < _cells.length; i++) { // But there might be some assignment problem that I missed
if (_cells[j][i] >= 4) {
_cells[j][i] = _cells[j][i] - 4;
if (j !== _cells.length - 1) _cells[j + 1][i]++;
if (j !== 0) _cells[j - 1][i]++;
if (i !== _cells.length - 1) _cells[j][i + 1]++;
if (i !== 0) _cells[j][i - 1]++;
}
}
}
return _cells;
}
function fill2DarrFromArr(_arr) {
let sideLength = Math.sqrt(_arr.length);
let out = create2DArr(sideLength, sideLength);
for (let j = 0; j < sideLength; j++) {
for (let i = 0; i < sideLength; i++) {
out[j][i] = _arr[j * sideLength + i];
}
}
return out;
}
function create2DArr(_n, _m) {
let _arr = new Array(_n);
for (let j = 0; j < _m; j++) {
_arr[j] = new Array(_m);
}
return _arr;
}
function fillEmpty(_arr) {
for (let j = 0; j < _arr.length; j++) {
for (let i = 0; i < _arr.length; i++) {
if (!_arr[j][i]) _arr[j][i] = 0;
}
}
return _arr;
}
Passing an array into a function doesn't create a copy of that array. Your functions are modifying the contents of the passed arrays, therefore they have side effects.

Performance: why is the first implementation of the same algorithm significantly faster

The algorithm is taken from LeetCode: https://leetcode.com/problems/maximum-product-of-word-lengths/description/
Here is the jsperf I created (I have some local tests which gives the same result): https://jsperf.com/maximum-product-of-word-lengths
Here is the first "slow" implementation:
function maxProduct (words) {
if (!words || !words.length) return 0;
let len = words.length;
let values = [];
// console.log(values)
for (let i = 0; i < len; ++i) {
let tmp = words[i];
let num = 0, len = tmp.length;
for (let j = 0; j < len; ++j) {
num |= 1 << (tmp.charCodeAt(j) - 'a'.charCodeAt(0));
}
values[i] = {
num: num,
len: tmp.length
};
}
let maxProduct = 0;
for (let i = 0; i < len; ++i) {
for (let j = 0; j < len; ++j) {
if ((values[i].num & values[j].num) == 0) {
maxProduct = Math.max(maxProduct, values[i].len * values[j].len);
}
}
}
return maxProduct;
};
Here is the "fast" implementation:
function maxProductFast (words) {
var temp = [];
for(var i = 0; i < words.length; i++){
var tempObj = {};
tempObj.item = words[i];
var num = 0;
for(var j = 0; j < words[i].length; j++){
num |= 1 << (words[i].charCodeAt(j) - 97);
}
tempObj.num = num;
temp.push(tempObj);
}
var res = 0;
for(var i = 0; i < temp.length; i++){
for(var j = i + 1; j < temp.length; j++){
var item1 = temp[i];
var item2 = temp[j];
if((item1.num & item2.num) == 0) {
res = Math.max(res, item1.item.length * item2.item.length);
}
}
}
return res;
}
They're not the same. The second algorithm has a loop with a complexity of (n*(n+1))/2 where each progressive step is from i+1 to the length of temp. the first algorithm has a two nested for loops each with a cost of n^2. the complexity of both will reduce to O(n^2). I believe that both of these will have a similar performance with a significantly large enough set.
The reason you would do n+1 for each sub iteration is because you are trying to find the max of any pair of items. if you place your elements in a grid you will notice that any diagonal pair a_3 * a_2 = a_2 * a_3 produces the same value. you can basically halve the collection and save a few cycles.

Creating a function to combine a nested array without recursion

I have the following array as an example;
let arr = [['red','blue','pink],['dog','cat','bird'],['loud', 'quiet']]
I need to write a generalized function that prints all combinations of one word from the first vector, one word from the second vector, etc. I looked up some codes on here but they are all recursion or working only with the specific array. How can I write this code without recursion?
let allComb = function(arr) {
if (arr.length == 1) {
return arr[0];
} else {
let result = [];
let arrComb = allComb(arr.slice(1));
for (let i = 0; i < arrComb.length; i++) {
for (let j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + ' ' + arrComb[i]);
}
}
return result;
}
}
allComb(arr)
This version uses a single increment per cycle technique with no recursion.
let arr = [
['red', 'blue', 'pink'],
['dog', 'cat', 'bird'],
['loud', 'quiet']
];
function allComb(arr) {
var total = 1;
var current = [];
var result = [];
for (var j = 0; j < arr.length; j++) {
total *= arr[j].length;
current[j] = 0;
}
for (var i = 0; i < total; i++) {
var inc = 1;
result[i] = "";
for (var j = 0; j < arr.length; j++) {
result[i] += arr[j][current[j]] + ' ';
if ((current[j] += inc) == arr[j].length)
current[j] = 0;
else
inc = 0;
}
}
return (result);
}
console.log(allComb(arr));
You may do as follows;
var arr = [['red','blue','pink'],['dog','cat','bird'],['loud', 'quiet']],
res = arr.reduce((p,c) => p.reduce((r,x) => r.concat(c.map(y => x + " " + y)),[]));
console.log(res);

Categories

Resources