tags:

views:

3163

answers:

5

...where each object also have references to other objects within the same array? When I first came up with this problem I just though of something like

var clonedNodesArray = nodesArray.clone()

would exists and searched for info on how cloning objects in javascript. I did find a question on StackOverflow (answered by the very same John Resig) and he pointed out that with jQuery you could do

var clonedNodesArray = jQuery.extend({}, nodesArray);

to clone an object. I tried this though, this only copies the references of the objects in the array. So if I

nodesArray[0].value = "red"
clonedNodesArray[0].value = "green"

the value of both nodesArray[0] and clonedNodesArray[0] will turn out to be "green". Then I tried

var clonedNodesArray = jQuery.extend(true, {}, nodesArray);

which deep copies an Object, but I got "too much recursion" and "control stack overflow" messages from both Firebug and Opera Dragonfly respectively.

How would you do it? Is this something that shouldn't even be done? Is there a reusable way of doing this in javascript?

A: 

Array.slice can be used to copy an array or part of an array.. http://www.devguru.com/Technologies/Ecmascript/Quickref/Slice.html This would work with strings and numbers .. - changing a string in one array would not affect the other - but objects are still just copied by reference so changes to referenced objects in one array would have an affect on the other array.

Here is an example of a JavaScript undo manager that could be useful for this :http://www.ridgway.co.za/archive/2007/11/07/simple-javascript-undo-manager-for-dtos.aspx

markt
I know. The reason I wanted to implement this is because I'm trying to resolve a CSP problem with backtracking. I thought that one of the ways of implementing backtracking could be like "taking snapshots" the state of the assignment of the variables by... cloning such snapshots into a stack.
wallyqs
...and well, it might actually be a very bad idea.
wallyqs
That approach could have other synchronization complications :).. How do you know the array is not being changed *while* you are taking a snapshot?
markt
Added a link to an article where the author implemented a simple undo manager using javascript..
markt
+4  A: 

The issue with your shallow copy is that all the objects aren't cloned. While the references to each object are unique in each array, once you ultimately grab onto it you're dealing with the same object as before. There is nothing wrong with the way you cloned it... the same result would occur using Array.slice().

The reason your deep copy is having problems is because you're ending up with circular object references. Deep will go as deep as it can go, and if you've got a circle, it'll keep going infinitely until the browser faints.

If the data structure cannot be represented as a directed acyclic graph, then I'm not sure you're going to be able to find an all-purpose method for deep cloning. Cyclic graphs provide many tricky corner cases, and since it's not a common operation I doubt anyone has written a full solution (if it's even possible - it might not be! But I have no time to try to write a rigorous proof now.). I found some good comments on the issue on this page.

If you need a deep copy of an Array of Objects with circular references I believe you're going to have to code your own method to handle your specialized data structure, such that it is a multi-pass clone:

  1. On round one, make a clone of all objects that don't reference other objects in the array. Keep a track of each object's origins.
  2. On round two, link the objects together.
Daniel Lew
" There’s no way to write a generic deep-clone mechanism that works for all cases." hmmm... so it is not that simple.
wallyqs
A: 

As Daniel Lew mentioned, cyclic graphs have some problems. If I had this problem I'd either add special clone() methods to the problematic objects or remember which objects I've already copied.

I'd do it with a variable copyCount which increases by 1 every time you copy in your code. An object that has a lower copyCount than the current copy-process is copied. If not, the copy, that exists already, should be referenced. This makes it necessary to link from the original to its copy.

There is still one problem: Memory. If you have this reference from one object to the other, it's likely that the browser can't free those objects, as they are always referenced from somewhere. You'd have to make a second pass where you set all copy-references to Null. (If you do this, you'd not have to have a copyCount but a boolean isCopied would be enough, as you can reset the value in the second pass.)

Georg
+1  A: 

I may have a simple way to do this without having to do painful recursion and not knowing all the finer details of the object in question. Using jQuert, simply convert your object to JSON using the jQuery $.toJSON(myObjectArray), then take your JSON string and evaluate it back to an object. BAM! Done, and done! Problem solved. :)

var oldObjArray = [{ Something: 'blah', Cool: true }];

var newObjArray = eval($.toJSON(oldObjArray));

George
Some modern browsers have the JSON method built-in so you can do this:JSON.parse(JSON.stringify(MY_ARRAY))which should be faster. Good suggestion.
snz3
+2  A: 

$.evalJSON($.toJSON(origArray));

elsereturn