Cloning (deep copy) of an object

2,558 views
Skip to first unread message

Bob Monteverde

unread,
Jan 24, 2012, 4:18:12 PM1/24/12
to d3-js
So, I've come to love jquery's ability to deep copy objects via
$.extend(...), wondering if anyone has a quick solution to accomplish
this with d3 (my original protovis and d3 code used jquery in
multiple places, but considering how much I like d3, I've gone through
all my code and made it not dependent on any other frameworks.


The use case I have for this is, I'm created an indented tree (similar
to example) but in HTML not SVG. My issue is, when I allow a user to
fold a section, in SVG I could just remove the exit selection and the
enter selection worked great (this is because while each node is
actually added to the end of the list of node, they are essentially
absolutely positioned via translate). In my HTML version, which build
a table, adding to the end of the list, adds to the end of the list
visually. My initial fix for this was to instead of removing the exit
selection, simply setting "display: none" on the exit nodes. This
works IF all the nodes are rendered at the beginning. BUT I have a
few trees where I actually want to start folded, or fold at a certain
level, so the list is not overwhelming to the user until they dig down
themselves. My current idea for a solution is to take the initial
data structure ( { name: "label to show on node", children:
[ ...repeat...] }). Now if I want the children folded, I simply
rename "children" to "_children". NOW my idea is, on the first call
to build the tree, copy the initial data, rename ALL _children to
children, generate, then run update with the original data structure
to hide the folded nodes.

I'm thinking my solution is not ideal, but it SHOULD work. I'd love
to hear if anyone else has a good strategy. And for the time being, I
might be lazy and use jQuery.extend, and I suppose I can write a
recursive deep copy specific for my use case to get rid of the jQuery
dependence.

Any/All advice is welcome... even if you're just gonna call me a
fucking moron for such a bad implementation ;)

Mike Bostock

unread,
Jan 24, 2012, 4:20:55 PM1/24/12
to d3...@googlegroups.com
> My current idea for a solution is to take the initial
> data structure ( { name: "label to show on node", children:
> [ ...repeat...] }).  Now if I want the children folded, I simply
> rename "children" to "_children".

Like these?

http://bl.ocks.org/1249394
http://bl.ocks.org/1093025
http://mbostock.github.com/d3/talk/20111018/tree.html

Also, you can use Object.create to create a shallow copy:

https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create

Mike

Bob Monteverde

unread,
Jan 24, 2012, 6:11:00 PM1/24/12
to d3-js
I'm working off of the second example you posted, BUT generating an
HTML table (basically doing this so I can utilize some of the "grid"
code I have written for the data tables we have on our platform, as
well as allow for some IE support by using HTML instead of SVG)...
also HTML renders a little quicker ;)
Still trying to figure out the best way for enter and exit due to the
fact that enter doesn't insert in the same order.
>  https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/...
>
> Mike

Bob Monteverde

unread,
Jan 24, 2012, 6:11:42 PM1/24/12
to d3-js
Oh, and yeah, guess I'll make a simple loop to recursively copy (deep
copy essentially) for now.

Bob Monteverde

unread,
Jan 24, 2012, 7:27:49 PM1/24/12
to d3-js
Damn, my idea of unfolding all the data, the folding on the first call
fails... I guess its has something to do with that I'm using an
entirely different array (a deep copy of the original), then using the
original. Any insight on some trick I may be able to use to
accomplish what I'm tryign to?

On Jan 24, 4:20 pm, Mike Bostock <mbost...@cs.stanford.edu> wrote:
>  https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/...
>
> Mike

Bob Monteverde

unread,
Jan 25, 2012, 5:31:41 PM1/25/12
to d3-js
OK, if anyone's interested, I finally came up with a workign solution
(while some what funky... after 3 tries, best I could think of)



//The following is all done outside of "update()"....


var levels = getDepth(data, 0); //used to re-fold the children   
data = unfoldChildren(data); //unfolding for first render, then
refolding immediately after to fix bug with rows out of order

    function getDepth(obj, level) {      var depths;
      if (obj.children) {        depths =
obj.children.map(function(elem) {          return getDepth(elem, level
+ 1);        });      } else {        depths = [level];      }
      return d3.max(depths);    }
    function unfoldChildren(obj) {      if (obj._children) {       
obj.children = obj._children;        delete obj._children;      }     
if (obj.children) {        obj.children.forEach(function(elem) {     
     elem = unfoldChildren(elem);        });      }      return obj; //
technically this mutates the input anyway....    }
    function foldChildren(obj, d) {      if (obj.children) {       
obj.children.forEach(function(elem) {          elem =
foldChildren(elem, d - 1);        });        if (d <= 0) {         
obj._children = obj.children;          delete obj.children;        } 
    }      return obj; //technically this mutates the input
anyway....    }

var first = true;

update();

function update() {

//...... VERY similar to the second example posted by Mike, BUT in
my case rendering a html table, not svg

//instead of removing, need to just hide, otherwise rows are
appending to END of table, not appending to where they should be...
not an issue in SVG because every node is translated to the correct
position
node.exit()
.style('display', 'none')
.classed('hidden', true)
.classed('highlight', false);


//AFTER the unfolded tree renders, re-fold the data, then re-
render imediately
if (first) {
first = false;
data = foldChildren(data, levels);
update();
}
}




Definitely interested if anyone has a better solution.. BUT at least
this works

Bob Monteverde

unread,
Jan 25, 2012, 5:32:28 PM1/25/12
to d3-js
UGH, guess my spacing did not come out right.. is there a a way to
wrap a code block to keep white space?

Mike Bostock

unread,
Jan 25, 2012, 7:22:26 PM1/25/12
to d3...@googlegroups.com
> UGH, guess my spacing did not come out right.. is there a a way to
> wrap a code block to keep white space?

Yes. Next time, link to gist.github.com. ;)

Mike

Ian Johnson

unread,
Jan 25, 2012, 7:22:45 PM1/25/12
to d3...@googlegroups.com
you could put it in a gist at gist.github.com

then once you are familiar with that you can start using bl.ocks.org in the future :)
--
Ian Johnson

Bob Monteverde

unread,
Jan 31, 2012, 7:06:19 PM1/31/12
to d3-js
Finally figured out a much less retarded way to accomplish this. I
was unaware of the 'selection.order()' function... that was really
what I needed the whole time.

At least I learned something new today.
Reply all
Reply to author
Forward
0 new messages