1

I'm having a problem where I create a two-dimensional array (called "grid") that contains an object (the "square"). In this simplified example the object just keeps track of how many times the square has been selected (clicked).

My initial grid data looks like this after it's created...

this.grid = [   
    [{"selected":0},{"selected":0},{"selected":0}]
    ,[{"selected":0},{"selected":0},{"selected":0}]
    ,[{"selected":0},{"selected":0},{"selected":0}]  
];

Currently when a square is clicked and I try to increment the selected value in only one object, using something as simple as this.grid[x][y].selected += 1;. Instead all values in the row are incremented and I end up with...

[   [{"selected":0},{"selected":1},{"selected":0}]
    ,[{"selected":0},{"selected":1},{"selected":0}]
    ,[{"selected":0},{"selected":1},{"selected":0}]  ]

An entire row had selected incremented, not just one object. Not what I want. :(

I believe the problem is with the grid's array creation, because it works if I hard-code the grid's value. What might be causing the problem and what is the solution?

Here's the JavaScript code...

function GridClass () {
    this.$grid = $('.grid');
    this.grid = [[]]; 

    this.createGrid = function () { // I think this is where the problem lies
        var baseSize = 3;
        this.grid = [];
        var blankYArray = [];
        for (var y = 0; y < baseSize; y++) {
            blankYArray.push({
                "selected" : 0
            });
        }
        for (var x = 0; x < baseSize; x++) {
            this.grid.push(blankYArray);
        }
    }

    this.selectSquare = function (x, y) {
        this.grid[x][y].selected += 1;
        this.drawGrid();
    }

    this.drawGrid = function () {
        var h = "", rowClass = "";
        var xLen = this.grid.length;
        var yLen = this.grid[0].length;
        for (var y = 0; y < yLen; y++) {
            for (var x = 0; x < xLen; x++) {
                var g = this.grid[x][y];
                h += '<div class="' + rowClass 
                + ' alt' + g.selected + '"'
                + ' data-x="' + x + '"'
                + ' data-y="' + y + '">'
                + x + "," + y + '</div>';
            }
            h += '<br />';
        }
        this.$grid.html(h);
    }

    this.createGrid();
    this.drawGrid();
}
grid = new GridClass();

grid.$grid.on("click", "div", function(e){ // Click event for each square
    var $thisSquare = $(this);
    var x = $thisSquare.data("x");
    var y = $thisSquare.data("y");
    grid.selectSquare(x, y);
});

And the HTML is just: <div id="gridFrame"><div class="grid"></div></div>

The whole thing (including the CSS) is on jsfiddle here: http://jsfiddle.net/luken/fu4yW/

Luke
  • 18,811
  • 16
  • 99
  • 115

2 Answers2

1

What Sergio said, only try creating a new array each time in the loop...

for (var x = 0; x < baseSize; x++) {
        var blankYArray = [];

        for (var y = 0; y < baseSize; y++) {
            blankYArray.push({
                "selected" : 0
            });
        }

            this.grid.push(blankYArray);
        }

fiddle: http://jsfiddle.net/larsmaultsby/fu4yW/8/

user1133128
  • 164
  • 3
  • 1
    Thanks, this does work! But isn't there a way to just copy the array? (Not that performance is an issue, this solution does n^2 loops when I think it should be possible in 2n.) – Luke Mar 07 '14 at 20:21
  • 1
    Here's a relevant stackoverflow: http://stackoverflow.com/questions/9885821/copying-of-an-array-of-objects-to-another-array-without-object-reference-in-java and an update to the fiddle: http://jsfiddle.net/larsmaultsby/5L8rC/1/ – user1133128 Mar 07 '14 at 20:47
  • That seems like a workable solution. I wish JavaScript had some kind of 'clone object by value' function rather than making us resort to hacks using JSON. – Luke Mar 07 '14 at 20:58
  • btw - you should edit your answer to add that last comment. I think that is a good key to understanding the problem: the array contains an object so cannot simply be copied. – Luke Mar 07 '14 at 21:02
0

You are adding the same array several times to the grid, change your code to:

for (var x = 0; x < baseSize; x++) {
  this.grid.push(blankYArray.slice(0));
}

The slice method will return a sub array of of the original. slice(0) simply returns a new one with the same values as the original.

Update

Actually this solution won't work for an objects array. The reason is the array contains objects, so the method slice will return a reference to a new array, but that array still will contain references to the original array objects. In order to make this work you will need to create a new array in each iteration with different objects (like user1133128's answer):

for (var x = 0; x < baseSize; x++) {
    var blankYArray = [];
    for (var y = 0; y < baseSize; y++) {
        blankYArray.push({
            "selected" : 0
        });
    }
    this.grid.push(blankYArray);
}
S. A.
  • 3,714
  • 2
  • 20
  • 31
  • Thanks! I knew it was something "simple". So the `blankYArray` array is passed by reference to the push function? – Luke Mar 07 '14 at 19:54
  • Yes, I've updated the answer with the explanation of this behavior. – S. A. Mar 07 '14 at 21:47