Converting an object to be used with a treegrid 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?

JSBin

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 &lt; 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 &lt; 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 &lt; 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 functions

function 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) + '%';
}
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 function
function 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 fallbacks

function 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) + '%';
}




Addendum

If you need to handle the conversion differently for different levels of nesting (re: the comments), you could do something like this

function 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.