patternjavascriptMinor
Converting an object to be used with a treegrid
Viewed 0 times
treegridwithusedconvertingobject
Problem
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 < 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 gr
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 < 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 gr
Solution
I believe this does the same as yours, except it uses
Addendum
If you need to handle the conversion differently for different levels of nesting (re: the comments), you could do something like this
%%CODEBLOCK_1%%
I don't know if there's a system in the data you're converting, but you could also check for
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 = '
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. + earned.toFixed(2);
leaf.spent = '
Addendum
If you need to handle the conversion differently for different levels of nesting (re: the comments), you could do something like this
%%CODEBLOCK_1%%
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. + 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 '
Addendum
If you need to handle the conversion differently for different levels of nesting (re: the comments), you could do something like this
%%CODEBLOCK_1%%
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. + ((e - (cl * s)) / cl).toFixed(3);
}
function netEarned(e, s) {
if( !e ) return '$0.00';
return '
Addendum
If you need to handle the conversion differently for different levels of nesting (re: the comments), you could do something like this
%%CODEBLOCK_1%%
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. + (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
%%CODEBLOCK_1%%
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.Code Snippets
// 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) + '%';
}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;
}Context
StackExchange Code Review Q#16136, answer score: 2
Revisions (0)
No revisions yet.