My problem is this:
Suppose this class, it's an example of my true code:
class TileMap {
constructor ( w, h ) {
this.tiles = [];
// init the matrix
for (var i = 0; i < h; i++) {
var a = [];
for (var j = 0; j < w; j++) {
a.push(0);
}
this.tiles[i] = a;
}
}
setTile (x, y, tile) {
this.tiles[y][x] = tile;
}
doSomething () {
this.setTile(0, 0, 1);
this.setTile(0, 1, 2);
}
}
// What's happening is when I use like this:
var player = {};
player.map = new TileMap(32, 90, 90);
player.map.doSomething();
console.log("before Tile[0][0] = " + player.map.tiles[0][0]);
player.map.setTile(0, 0, 3);
console.log("after Tile[0][0] = " + player.map.tiles[0][0]);
Shows me the follow output:
before Tile[0][0] = 1
after Tile[0][0] = 1
The matrix are modified but turn back to before values.
What should I do? (NOTE I'm not familiar with javascript but with language like C++)
I do not understand what is wrong. I added to your code the player object.
class TileMap{
constructor ( w, h ){
this.tiles = [];
//init the matrix
for (var i = 0; i < h; i++)
{
var a = [];
for (var j = 0; j < w; j++)
a.push(0);
this.tiles[i] = a;
}
}
setTile (x, y, tile){
this.tiles[y][x] = tile;
}
doSomething (){
this.setTile(0,0, 1);
this.setTile(0,1, 2);
}
}
//What's happening is when I use like this:
var player = {};
player.map = new TileMap(32,90,90);
player.map.doSomething();
console.log("before Tile[0][0] = "+player.map.tiles[0][0]);
player.map.setTile(0,0, 3);
console.log("after Tile[0][0] = "+player.map.tiles[0][0]);
Related
I'm new to JavaScript, I'm trying to solve leetcode question 37. I need to a create a blank two dimensional array, I initially used the method in the comments; however, it doesn't work correctly, it will change all the value. Then, I used the for loop method to create array and currently it worked correctly. But I still cannot figured out why this will happen, could anyone explain the reason why this will happen, is this because of shallow copy?
var solveSudoku = function (board) {
// let rows = new Array(9).fill(new Array(10).fill(0)),
let rows = new Array(9);
for (let i = 0; i < 9; i++) {
rows[i] = new Array(10).fill(0);
}
let cols = new Array(9);
for (let i = 0; i < 9; i++) {
cols[i] = new Array(10).fill(0);
}
let boxes = new Array(9);
for (let i = 0; i < 9; i++) {
boxes[i] = new Array(10).fill(0);
}
// let cols = new Array(9).fill(new Array(10).fill(0)),
// boxes = new Array(9).fill(new Array(10).fill(0));
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
let c = board[i][j];
if (c !== '.') {
let n = parseInt(c),
bx = Math.floor(j / 3),
by = Math.floor(i / 3);
// 0代表为使用,1为使用过
rows[i][n] = 1;
console.log(i, n)
cols[j][n] = 1;
// box索引
boxes[by * 3 + bx][n] = 1;
}
}
}
fill(board, 0, 0)
function fill(board, x, y) {
// 完成填充条件
if (y === 9) return true;
// 下一个点的坐标
let nx = (x + 1) % 9,
// 判断进入是否下一行
ny = (nx === 0) ? y + 1 : y;
// 如果已经填充,则进入下一个点
if (board[y][x] !== '.') return fill(board, nx, ny);
// 没有被填充过
for (let i = 1; i <= 9; i++) {
let bx = Math.floor(x / 3),
by = Math.floor(y / 3),
box_key = by * 3 + bx;
if (!rows[y][i] && !cols[x][i] && !boxes[box_key][i]) {
rows[y][i] = 1;
cols[x][i] = 1;
boxes[box_key][i] = 1;
board[y][x] = i.toString();
console.log(board[y][x])
// 递归向下一个点求解
if (fill(board, nx, ny)) return true;
// 恢复初始状态
board[y][x] = '.';
boxes[box_key][i] = 0;
rows[y][i] = 0;
cols[x][i] = 0;
}
}
return false;
}
console.log(board);
};
The problem with fill(), at least with object, is that it passes the same object, by reference, to all element of the array. So if you mutate this object, then it will mutate every object of every arrays.
Note that in your case, you are creating a new Array object using it's constructor ( new Array() ) which makes them objects.
const matrix = new Array(5).fill(new Array(5).fill(0));
console.log(matrix);
In the previous snippet, you can see that the values of the other rows, from the second one to the end, are reference to the initial row.
To get around that, you can fill you array with empty values and then use the map() to create unique object for each position in the array.
const matrix = new Array(5).fill().map(function() { return new Array(5).fill(0); });
console.log(matrix);
As you can see in the previous snippet, all the rows are now their unique reference.
This is the reason all of your values were changed.
I've applied this solution to your code. I wasn't able to test it, because I wasn't sure of the initial parameters to pass.
I've also used anonymous function here ( function() { return; } ), but I would success using arrow function ( () => {} ) instead, if you are comfortable with them. It's cleaner.
var solveSudoku = function (board) {
let rows = new Array(9).fill().map(function() { return new Array(10).fill(0); }),
cols = new Array(9).fill().map(function() { return new Array(10).fill(0); }),
boxes = new Array(9).fill().map(function() { return new Array(10).fill(0); });
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
let c = board[i][j];
if (c !== '.') {
let n = parseInt(c),
bx = Math.floor(j / 3),
by = Math.floor(i / 3);
// 0代表为使用,1为使用过
rows[i][n] = 1;
console.log(i, n)
cols[j][n] = 1;
// box索引
boxes[by * 3 + bx][n] = 1;
}
}
}
fill(board, 0, 0)
function fill(board, x, y) {
// 完成填充条件
if (y === 9) return true;
// 下一个点的坐标
let nx = (x + 1) % 9,
// 判断进入是否下一行
ny = (nx === 0) ? y + 1 : y;
// 如果已经填充,则进入下一个点
if (board[y][x] !== '.') return fill(board, nx, ny);
// 没有被填充过
for (let i = 1; i <= 9; i++) {
let bx = Math.floor(x / 3),
by = Math.floor(y / 3),
box_key = by * 3 + bx;
if (!rows[y][i] && !cols[x][i] && !boxes[box_key][i]) {
rows[y][i] = 1;
cols[x][i] = 1;
boxes[box_key][i] = 1;
board[y][x] = i.toString();
console.log(board[y][x])
// 递归向下一个点求解
if (fill(board, nx, ny)) return true;
// 恢复初始状态
board[y][x] = '.';
boxes[box_key][i] = 0;
rows[y][i] = 0;
cols[x][i] = 0;
}
}
return false;
}
console.log(board);
};
My code currently has a bug where my 2-d array with the bool value false suddenly contains true values before it is assigned any. My current guesses is either console.log somehow is delayed and picks up the values after it is called, with the updated values or that there is some issue that I don't understand about how scope works in javascript.
As seen below console.log(visited[i][j]) results in false for all values but the
new visited line contains true values even before the following is called.
const field_size = 800;
const cells_in_row = 5;
const frames_per_second = 1;
const cell_size = field_size / cells_in_row;
class Cell {
constructor(x,y) {
this.value = 0;
this.x = x;
this.y = y;
this.coordinates = [x*cell_size,y*cell_size];
}
fill() {
this.value = 1;
}
clear() {
this.value = 0;
}
}
const get_new_grid = (random = 0) => {
const grid = new Array(cells_in_row);
for (let i = 0; i < grid.length; i++) {
grid[i] = new Array(cells_in_row);
for (let j = 0; j < grid.length; j++) {
grid[i][j] = new Cell(i,j);
v = 0;
if (random) {
v = Math.floor(Math.random() * 2);
}
grid[i][j].value = v;
}
}
return grid;
}
const get_islands = (grid) => {
// bool array to mark visited cells
let visited = new Array(cells_in_row);
for (let i = 0; i < grid.length; i++) {
visited[i] = new Array(cells_in_row);
for (let j = 0; j < grid[0].length; j++) {
visited[i][j] = false;
}
}
console.log("New Visited", visited);
let count = 0;
let islands = [];
let island_coords = [];
for (let i = 0; i < grid.length; i++) {
for (let j = 0; j < grid.length; j++) {
if (visited[i][j] == false && grid[i][j].value == 1) {
// visit all cells in this island and increment island count
// dfs will return array of coordinates of island
[visited, island_coords] = dfs(i, j, grid, visited, island_coords);
console.log(visited);
islands.push(island_coords);
count += 1;
}
}
}
return [count, islands];
}
const dfs = (i, j, grid, visited, island_coords) => {
let row_nbr = [-1, -1, -1, 0, 0, 1, 1, 1];
let col_nbr = [-1, 0, 1, -1, 1, -1, 0, 1];
visited[i][j] = true;
island_coords.push([i,j]);
for (let k = 0; k < 8; k++) {
if (is_safe(i + row_nbr[k], j + col_nbr[k], grid, visited)) {
console.log("DFSing " + i + "," + j);
[visited, island_coords] = dfs(i + row_nbr[k], j + col_nbr[k],
grid, visited, island_coords);
}
}
return [visited, island_coords];
}
const is_safe = (i, j, grid, visited) => {
return (i >= 0 && i < grid.length &&
j >= 0 && j < grid.length &&
!(visited[i][j]) && grid[i][j].value === 1);
}
(function () {
var old = console.log;
var logger = document.getElementById('log');
console.log = function () {
for (var i = 0; i < arguments.length; i++) {
if (typeof arguments[i] == 'object') {
logger.innerHTML += (JSON && JSON.stringify ? JSON.stringify(arguments[i], undefined, 2) : arguments[i]) + '<br />';
} else {
logger.innerHTML += arguments[i] + '<br />';
}
}
}
})();
window.onload = () => {
const canvas = document.getElementById('canvas');
const grid = get_new_grid(random = 0);
grid[0][0].value = true;
grid[0][1].value = true;
grid[1][0].value = true;
grid[1][1].value = true;
const islands = get_islands(grid);
console.log(grid);
console.log(islands);
}
<!DOCTYPE html>
<html>
<body>
<script src="gameoflife.js"></script>
<pre id="log"></pre>
</body>
</html>
EDIT:
So I updated the snippet but it looks like it works on this end, however it shows the behavior I mentioned before on my own browser even with the exact same javascript code and html in the snippet.
Mentioned in the comments by Niet, objects logged to the console are live.
I get convergence, nevertheless, the results are never the same when the algorithm is refreshed. This occurs even when the data observations is the same dataset. Can anyone tell me where my methodology is wrong? For the life of me I can't figure out where the process is wrong.
function kmeans2(k, data, canvas, converge) {
this.canvas = jsHS.GetDimensions(canvas);
this.k = k;
this.centroids = []; // Array of centroids
this.centroids2compare = [];
this.data = data;
this.converge = converge;
this.init();
}
kmeans2.prototype.distance = function () {
var dif = 0,
iArray = jsHS.isArray(arguments);
if (iArray) {
if (arguments.length > 2) {
for (var i = 0; i < arguments.length; i+2) {
var p0 = arguments[i],
p1 = arguments[i + 1];
dif += Math.pow(p0[0] - p1[0], 2);
dif += Math.pow(p0[1] - p1[1], 2);
}
}
else {
var pd0 = arguments[0],
pd1 = arguments[1];
dif += Math.pow(pd0[0] - pd1[0], 2);
dif += Math.pow(pd0[1] - pd1[1], 2);
}
}
return Math.sqrt(dif);
};
kmeans2.prototype.Means = function (Array) {
var bin = 0;
[].forEach.call(Array, function(a){
bin += a;
});
return bin / Array.length;
};
kmeans2.prototype.init = function () {
for (var l = 0; l < this.k; l++) {
var dataItem = this.data[Math.floor(Math.random() * this.data.length)];
this.centroids.push(dataItem);
}
for (var i = 0; i < this.centroids.length; i++) {
if (i > 0) {
var distance = this.distance(this.centroids[i], this.centroids[i - 1]);
console.log(distance);
}
}
this.clusterCentroids(); // return centroid center after calculating means.
};
kmeans2.prototype.clusterCentroids = function () {
var points0 = [];
this.centroids2compare = this.centroids;
// Find distances between centroid and observations.
for (var d = 0; d < this.data.length; d++) {
var cinbin = [];
for (var c0 = 0; c0 < this.k; c0++) {
var dis = this.distance(this.centroids[c0], this.data[d]);
cinbin.push({ 'cid': c0, 'distance': dis });
}
var minResult = cinbin.reduce((cid, obj) => {
return obj.distance < cid.distance ? obj : cid;
});
points0.push({ 'id': d, 'datapoint': this.data[d], 'centroid': minResult.cid });
}
// Assign observations their appropriate centroid.
var centroidBin = [];
for (var c = 0; c < this.k; c++) {
var cb = [];
for (var p = 0; p < points0.length; p++) {
if (c === points0[p].centroid) {
cb.push(points0[p]);
}
}
centroidBin.push(cb);
}
// Calculate the mean distance between centroids and its assigned observations.
this.centroids = [];
for (var bin = 0; bin < centroidBin.length; bin++) {
var xAxis = [],
yAxis = [],
cb0 = centroidBin[bin];
[].forEach.call(cb0, function (dp) {
xAxis.push(dp.datapoint[0]);
yAxis.push(dp.datapoint[1]);
});
var xMean = this.Means(xAxis);
var yMean = this.Means(yAxis);
this.centroids.push([xMean, yMean]);
}
// Test for convergence. If stored centroids equal new centroids then convergence is achieved.
if (JSON.stringify(this.centroids2compare) !== JSON.stringify(this.centroids)) {
this.centroids2compare = [];
points0 = [];
this.clusterCentroids();
}
else {
this.converge(centroidBin, this.centroids);
}
};
window['jsHS']['kmeans2'] = kmeans2;
Implementation
var k50 = new jsi.kmeans2(5, Array50, canvas, function (con, centroids) {
var count50 = 0;
var cmark = {
x: 0,
y: 0,
rad:0,
clr: null,
setArc: function () {
ctx.beginPath();
ctx.arc(this.x, this.y, this.rad, 0, Math.PI * 2, true);
ctx.fillStyle = this.clr;
ctx.fill();
}
};
[].forEach.call(centroids, (c) => {
cmark.x = c[0];
cmark.y = c[1];
cmark.clr = '#0B6623';
cmark.rad = 25;
cmark.setArc();
});
});
This example plots the centroids on a canvas area fine enough but when the browser refreshes the centroids change.
I haven't looked much at your code, but I know that the k-means algorithm tends to give different results when you run it several times. This is because it's highly dependent on where the first centroids (which are selected randomly) are located.
The algorithm can find a local minimum and get "stuck" there, and terminate.
There's no guarantee that you will find the global minimum the first time you run it.
So I need few arrays:
array 1 = [1,9,17,25,33,41];
array 2 = [2,10,18,26,34,42];
etc.
So each array adds up 8 to the last item.
But, I need to generate this dynamically (using functions in JavaScript).
var initValue = 5;
var diff = 8;
var len = 5;
function makeDiffArray(initValue, diff, len) {
for (var i = 0, arr = []; i < len; i++) {
arr.push(initValue);
initValue += diff;
}
return arr;
}
console.log(makeDiffArray(initValue, diff, len));
Something like this?
for(var i = 1; i<10;i++){
eval("var array" + i + " = [" + i + "];");
for(var j = 1; j<10; j++){
eval("array" + i + ".push(array" + i + "[array" + i + ".length] + 8);");
}
}
You can also try dynamic variable names
var arrayCount = 2;
var initValue = 5;
var diff = 8;
var len = 5;
for(var i=1; i<=arrayCount; i++) {
window['array'+i] = makeAnArray(i,diff,len);
alert(window['array'+i]);
}
function makeAnArray(initValue) {
var anArray = [];
for (var j = 0, init = initValue; j < len; j++) {
anArray.push(init);
init += diff;
}
return anArray;
}
I know this has already been accepted, just wanted to add the ES6 version here.
let [init, diff, len] = [5, 8, 5], tmp = init;
let arr = Array(len).fill(init).map( (x, i) => (i) ? tmp += diff : x );
console.log(arr)
I need to implement DataTable struct ,that is in c#, in javascript.
For example
function Servers(name)
{
this.Name = name;
this.Columns = new Array(5);
var rows = new Array(3);
for (var i=0;i<3;i++)
rows[i]=new Array(5);
this.Rows = rows;
}
I simply access jth element of ith Row by typing like this;
Servers.Rows[i][j]
This works good, but I need to call my object like this;
Servers.Rows[i]["ServerUrl"]
But I dont know how to implement a prototype for this work.
Is there anyway to achieve this?
Note: Columns array holds Column names like in c# and Columns array size always equals to Rows' sub array.
Live demo
function create2Array(d1, d2, fn) {
var arr = [],
d = function(x, y) {},
f = fn || d;
for (var i = 0; i < d1; i++) {
for (var j = 0, curr = []; j < d2; j++) {
curr[j] = f.call(window, i, j);
};
arr[i] = curr;
};
return arr;
};
function createArrayOfObjects(d1) {
var arr = [];
for (var i = 0; i < d1; i++) {
arr[i] = {};
};
return arr;
};
function print2DArray(arr) {
document.body.innerHTML += "<p><b>Array:</b></p>";
for (var i = 0, len = arr.length; i< len; i++) {
document.body.innerHTML += "<p><b>" + i + "</b>: " + arr[i].join(" ") + "</p>";
};
};
function printArrayOfObj(arr) {
document.body.innerHTML += "<p><b>Array:</b></p>";
for (var i = 0, len = arr.length; i< len; i++) {
document.body.innerHTML += "<p><b>" + i + "</b>: " + JSON.stringify(arr[i]) + "</p>";
};
};
var Server = {};
Server.Rows = createArrayOfObjects(10);
Server.Rows[0]["something"] = "test";
printArrayOfObj(Server.Rows);
Use it like this:
Server.rows = create2Array(10, 10);
Or you can even specify a custom init function which takes the index as param.
Say if you want to init your matrix with 0 by default:
Server.rows = create2Array(10, 10, function(x, y) { return 0;});
Or if you use an object.
Server.rows = create2Array(10, 10);
Server.rows[0]["ServerUrl"] = "test";
You should use a hash/object for this.
If you want to refer to variables in a data structure by name/string in javascript the an object is the best option. See here: https://developer.mozilla.org/en/docs/JavaScript/Guide/Working_with_objects