0

I have a js game editor, and in order for it to have the undo function, I need to store the last couple states of the editors map. I need it to only push previous maps to stack if they are different form the first map on the stack, which means that maps will be pushed only when they are not the same as the last one. This stops copy maps from being pushed every frame, using up all the space in the stack, and having to undo tens of times to get back to where the human perception of the last state was.

At first, I thought it would be easy, just save the start condition of the map, do stuff, then save the end condition. If they are different, the user changed something, and the start condition should be push onto the stack. To undo, just set the current map to the last thing on the stack.

The problem is that some of these arrays seem to be linked, and are tripping up my function to check if they are equal.

//This code is part of the method that handles the mouse, 
//inside the editor class:
let start = this.map.slice(0);
//DO STUFF                            
let end = this.map.slice(0);
if(!ArrayIsEqual2d(start, end)){
  console.log('changed')
  this.lastMaps.unshift(start);
  if(this.lastMaps.length > this.undoBuffer){
    this.lastMaps.pop();
  }
}

//End code inside of editor class


function ArrayIsEqual2d(one, two){
    if(one == null || two == null || one == undefined || two == undefined){
        return false;
    }
    if(one.length !== two.length){
        return false;
    }
    
    for(let i = 0; i < one.length; i++){
        if(one[i].length !== two[i].length){
            return false;
        }
        for(let j = 0; j < one[i].length; j++){
            if(one[i][j] !== two[i][j]){
                return false;
            }
        }
    }
    return true;
}

The behavior is really weird, and it never detects that the map was changed, whats stranger is when I expose the start and end to the console, the value of start depends on where I log it. If it is logged right after it is created (before the user does stuff), its value is different then when I log it at the same time as end (after the user does stuff). Whenever I log it after the user does stuff, its value is always the same as map, which means that start and end are always the same during the check to see if anything has changed.

The only thing I could think of was that it was due to linking, but adding the .slice(0) in there changes nothing

Bagel03
  • 725
  • 7
  • 22
  • Does this answer your question? [How do you clone an Array of Objects in Javascript?](https://stackoverflow.com/questions/597588/how-do-you-clone-an-array-of-objects-in-javascript) – VLAZ Jun 26 '20 at 14:30

2 Answers2

1

Slice creates a shallow copy, meaning that the newly created array still refers to the original one.

Check: Does Javascript slice method return a shallow copy?

Nico
  • 157
  • 1
  • 15
  • Does it reference the whole array, or just the nested arrays? – Bagel03 Jun 26 '20 at 14:24
  • "For objects and arrays containing other objects or arrays, copying these objects requires a deep copy. Otherwise, changes made to the nested references will change the data nested in the original object or array. This is compared to a shallow copy, which works fine for an object or array containing only primitive values, but will fail for any object or array that has nested references to other objects or arrays." Here is a very good article that will help you https://medium.com/javascript-in-plain-english/how-to-deep-copy-objects-and-arrays-in-javascript-7c911359b089 – Nico Jun 26 '20 at 14:28
1

It looks like map is an array of arrays, with the actual data in the inner arrays.

Therefore all let start = this.map.slice(0) does is copy an array of references to those inner arrays into an array start. Those references are now identical in map and start - it's just a bunch of (reference) values.

As you update the values in map, those references point to those same updated inner arrays in both map and start, since the references are identical.

So, even though map !== start (try it), map[0] === start[0] and also therefore map[0][0] === start[0][0], for any index. They're different arrays, they're not linked, but the data they contain is -- i.e. it's the same data.

davnicwil
  • 28,487
  • 16
  • 107
  • 123
  • This answer makes more sense, the other answer is correct, but this one does a better job explaining why – Bagel03 Jun 26 '20 at 14:51