I'm a bit novice/moderate at JS. To my surprise I managed to put this together and it does work for what I intended. The objective is to convert an object into a format that is more conducive to being used with a treegrid. I'm using the underscore library to grab object keys at various points.I just wanted to know if maybe there's a better or perhaps more performant way of looping through these deeply nested objects that I may be overlooking. Something that's capable of handling hundreds, or even a thousand, of these nodes without potentially locking up a browser? I found a hint about storing the length variables separately in a similar question that displayed looping through things in this manner, I just wanted to know if there may be any other tips?var data_source = {sprint:{children:{country:{us:{clicks:26}},device:{iphone:{clicks:26}},isp:{comcast cable:{clicks:26}},os:{ios:{clicks:15},android:{clicks:10}},referer:{unknown:{clicks:26}}},clicks:26},verizon:{children:{country:{us:{clicks:10,conversions:5,earned:0.5,spent:0.2}},device:{galaxy s:{clicks:1}},isp:{comcast cable:{clicks:1}},os:{android:{clicks:1}},referer:{unknown:{clicks:1}}},clicks:1,conversions:1}};var object = [];function convert(data){  var keys = _.keys(data);  var keyslength = keys.length;  for (var i = 0; i < keyslength; i++){    var me = data[keys[i]];    var rootleaf = buildLeaf(me);    rootleaf.name = keys[i];    rootleaf.children = [];    var childkeys = _.keys(me.children);    var childkeyslength = childkeys.length;    for (var i2 = 0; i2 < childkeyslength; i2++){      var childme = me.children[childkeys[i2]];      var childleaf = {};      childleaf.name = childkeys[i2];      childleaf.children = [];      var grandchildkeys = _.keys(childme);      var grandchildkeyslength = grandchildkeys.length;      for (var i3 = 0; i3 < grandchildkeyslength; i3++){        var grandchildme = childme[grandchildkeys[i3]];        var grandchildleaf = buildLeaf(grandchildme);        grandchildleaf.name = grandchildkeys[i3];        childleaf.children.push(grandchildleaf);            }      rootleaf.children.push(childleaf);         }    object.push(rootleaf);      }}convert(data_source);console.log(object);function buildLeaf(node){  var tempnode = {};  var clicks = ((node.clicks) ? node.clicks : 0);  var conversions = ((node.conversions) ? node.conversions : 0);  var earned = ((node.earned) ? node.earned : 0);  var spent = ((node.spent) ? node.spent : 0);  tempnode.clicks = clicks;  tempnode.conversions = conversions;  tempnode.earned = '$' + earned.toFixed(2);  tempnode.spent = '$' + spent.toFixed(2);  tempnode.conversion_rate = conversionRate(conversions,clicks);  tempnode.net_earned = netEarned(earned,spent);  tempnode.epc = epc(clicks,earned,spent);  tempnode.roi = roi(earned,spent);  return tempnode;}// calculation functionsfunction conversionRate(cv, cl) {    if (cl === 0) return '0%';    return ((cv/cl)*100).toFixed(1) + '%';}function epc(cl, e, s) {    if (cl === 0 || e === 0) return '$0.000';    return '$' + ((e - (cl * s)) / cl).toFixed(3);}function netEarned(e, s) {    if (e === 0) return '$0.00';    return '$' + (e - s).toFixed(2);}function roi(e, s) {    if (e === 0) return '0%';    return (((e - s) / s) * 100).toFixed(0) + '%';}Here's a jsbin link for posterity: http://jsbin.com/uwudaw/3/editThanks for any advice.
Better way to loop through deeply nested objects?
javascript;performance
I believe this does the same as yours, except it uses buildLeaf on each step (your function skips it in the middle loop, and just makes an empty object).// Recursive convertion functionfunction convert(obj) {  var item, leaf, key, leaves = [];  for( key in obj ) {    if( !obj.hasOwnProperty(key) ) continue;    item = obj[key];    leaf = buildLeaf(key, item)    leaves.push(leaf);    if( typeof item.children === 'object' ) {      leaf.children = convert(item.children);    }  }  return leaves;}// I also refactored buildLeaf slightly to accept both// name and content, and to make use of JavaScript's// `||`-style fallbacksfunction buildLeaf(name, node) {  var leaf        = { name: name, children: [] },      clicks      = node.clicks || 0,      conversions = node.conversions || 0,      earned      = node.earned || 0,      spent       = node.spent || 0;  leaf.clicks          = clicks;  leaf.conversions     = conversions;  leaf.earned          = '$' + earned.toFixed(2);  leaf.spent           = '$' + spent.toFixed(2);  leaf.conversion_rate = conversionRate(conversions,clicks);  leaf.net_earned      = netEarned(earned,spent);  leaf.epc             = epc(clicks,earned,spent);  leaf.roi             = roi(earned,spent);  return leaf;}// In the calculation functions, I made the value checking// less strict. Generally, strong checking is encouraged,// but in this case other useless values would slip through,// when only strongly checking for zero (since that allows// `false`, empty strings, etc. to pass through).function conversionRate(cv, cl) {  if( !cl ) return '0%';  return ((cv/cl)*100).toFixed(1) + '%';}function epc(cl, e, s) {  if( !cl || !e ) return '$0.000';  return '$' + ((e - (cl * s)) / cl).toFixed(3);}function netEarned(e, s) {  if( !e ) return '$0.00';  return '$' + (e - s).toFixed(2);}function roi(e, s) {  if( !e ) return '0%';  return (((e - s) / s) * 100).toFixed(0) + '%';}AddendumIf you need to handle the conversion differently for different levels of nesting (re: the comments), you could do something like thisfunction convert(obj) {  // get the extra, optional, argument.  // It defaults to 1, and increments otherwise  var level = (arguments[1] || 0) + 1;  var item, leaf, key, leaves = [];  for( key in obj ) {    if( !obj.hasOwnProperty(key) ) continue;    item = obj[key];    // Check the level here    if( level === 2 ) {      // 2nd level will only be converted to a simpler, non-leaf object      leaf = { name: key, children: [] };    } else {      // Other levels will become leaves      leaf = buildLeaf(key, item);    }    leaves.push(leaf);    if( typeof item.children === 'object' ) {      // remember to pass the level on      leaf.children = convert(item.children, level);    }  }  return leaves;}I don't know if there's a system in the data you're converting, but you could also check for level % 2 === 0 to handle every second (i.e. even-numbered) level differently.