4

In my Angular application, I have an array that refers to the coordinates of a polygon. Eg:

[[-1,0], [0,1], [1,0], [0,-1], [-1,0]]

The important bit here is that the that the first and last points are repeated, and actually reference the same 2-length array. This is a result of a plugin I'm using. However, some times the arrays will be created in such a way that the first and last points, while having the same value, are not the same reference.

At a certain point in my Angular application, I need to create a new polygon with the same coordinates as the original, only flipped. My first attempt was this:

var newCoords = angular.copy(polygon.coordinates);
for (var i = 0; i < newCoords.length; i++) {
  newCoords[i].reverse();
}

However, in those instances in which the first and last coordinates have the same reference, I was ending up with one of the points being reversed twice.

My understanding was that angular.copy() creates a deep copy of whatever is passed in, and I shouldn't be experiencing this issue. Clearly this is incorrect, so why? Is there a way to do a truly deep copy of the coordinates array that eliminates that odd reference pairing? I've managed to get around it for now by adding in an additional angular.copy(newCoords[i]) before the reverse().

Will Durney
  • 1,168
  • 2
  • 13
  • 16
  • can you reproduce the issue? for me is working using the same reference for me, is something like this what you have? http://jsfiddle.net/rahpuser/2ngtw4bw/2/ – rahpuser Nov 24 '14 at 15:05
  • It must be a bug with angular.copy() in my application's version of angular. If you look here (http://jsfiddle.net/2ngtw4bw/4/) this prints out `false true false` as expected. When I copy that code *exactly* into my application, it prints out `false true true`. – Will Durney Nov 24 '14 at 16:45
  • Actually, it seems like this may be a bug *fix* between Angular 1.2.1 (jsfiddle) and Angular 1.2.19 (my application). It depends on whether your definition of a deep copy includes maintaining relative references in the new object. The angular.copy doc should probably make this more clear. – Will Durney Nov 24 '14 at 17:01
  • you are right, after changing the version of angular in jsfiddle I'm getting the same result you got: false true true.. – rahpuser Nov 24 '14 at 17:12
  • Maybe this could be related (introduced in 1.3): https://github.com/angular/angular.js/commit/b59b04f98a0b59eead53f6a53391ce1bbcbe9b57 – glepretre Nov 27 '14 at 17:42

1 Answers1

1

As suggesting in the comments, this is related to a change inside of angular's core. This change was released in Angular v1.2.17. The bug fix was listed as:

angular.copy: support circular references in the value being copied (5c997209, #7618)

However, along with circular references, double references are also handled. To intentionally not handle the double references, you have a few options. Like you mentioned in your post, after preforming a copy, recopy the first index. Not particularly intuitive, but it will work fine in any case:

var newCoords = angular.copy(polygon.coordinates);
// Ensure unique reference for the first element
newCoords[0] = angular.copy(newCoords[0]);

Another option is just to use any Angular versions prior to v1.2.17 (even all the way back to v0.9.0), because their default behavior is to create two different clones for each reference.


For others that may need to do a deep copy, but do not know the exact location of which duplicate references are (and may even be in subobjects), there is another way that will work with Angular versions prior to v1.4.8. This is to pass a third argument into copy to disable circular reference handling. Note that this will work with all versions prior to v1.2.17 as well, because they will simply ignore the 3rd parameter and do their default behavior:

var stackSource = angular.extend([], {push:angular.noop});
var newCoords = angular.copy(polygon.coordinates, null, noopArray);

The third parameter is the undocumented stackSource. By overriding it's push method to do nothing, the circular reference detection is broken. Two important things to note is that cyclical references will error (like v1.2.16 and down), and this doesn't work in v1.4.8 and up due to a performance change. In those cases you would have to write your own deep copy function.

jgawrych
  • 3,322
  • 1
  • 28
  • 38