Table -> Checkbox tree recursion question

I've found several "ways to do this" (example) on Stackoverflow but I am struggling to make this work as my JS knowledge isn't great!

I'm trying to create a checkbox tree which requires format (below)

My current json format is similar too (second, below)

Required Format

Example of my JSON
image

Where Label = Name
and Value = ID
and the Parent/Child relationship will be from parent/name or ID apart from the top level which will be named from industryName/industryCode

So

[
{
"label": "Economy, Finance and Trade"
"value": "CL"
children: [{
"label":"GVA from Manufacturing"
"value" : 84
children: [{ ...}]
}]
}]

Hey @qlmoffat, happy to help here!

I'd first like to share our checkbox tree documentation which is really helpful for showing how the data is formatted for the different component properties.

Based on what you've sent, I plugged it into a test checkboxTree and got it to show up by adding commas at the end of the lines and putting quotes around "children". Here's what I typed followed by a screenshot:

[

{

"label": "Economy, Finance and Trade",

"value": "CL",

"children": [

{

"label":"GVA from Manufacturing",

"value" : 84

}

]

}

]

Is there anything specific I can help you with with your checkbox Tree?

Thanks Jay!

I wrote that text at the bottom; I'm comfortable with that.

I'm trying to write a javascript script to go from the second screenshot of 6500+ items to a correctly formatted tree.

I've got to this by writing in Python and then converting to JS..

@Jay if you can see any improvements, I'd appreciate feedback. current challenge is making this recursive so that it sorts through all 'levels'. The tree only goes to 2 currently, 'full usage' is 7 levels which varies between categories.

Placing this here in case it helps anyone in future:

let obj = []
let level = 1
let data = data.data // flatfile data with id & parent id relationship


data.map(i => {
 if(i.level == 1){
  let row = {}
  row.label = i.name;
  row.value = i.id;
  row.children = [];
  obj.push(row);
 }
})               

obj.forEach( o =>{
 data.forEach(i =>{
  if(o.value == i.parentId){
    let child = {}
    child.label = i.name;
    child.value = i.id;
    o.children.push(child)
  }
 })
})

return obj;

Hey @qlmoffat! :blush:

I took a stab at your question this afternoon and ran into a similar thing. We basically need some sort of recursive search. I just got it to work with multiple levels of nesting (tested with 1-5).

My code is ugly and inefficient (big O = :elephant:) and will break if a child is missing either a parentId or parentName (though this can be pretty easily fixed), but just in case this is helpful for anyone, here it is!

const data = [
  {name: 'epi', id: 1},{name: 'barley', id: 6}, {name: 'sparta', id: 2, parentId: 1, parentName: 'epi'},{name: 'nova', id: 3, parentId: 1, parentName: 'epi'}, {name: 'bao', id: 4, parentId: 3, parentName: 'nova'}, {name: 'larry', id: 5}, {name: 'monkey', id: 7, parentId: 4, parentName: 'bao'}
];

const formatted_data = []

function nodeSearch(currentArr, loneChild, found = false, level=0) {

  if (found) return currentArr;
  if (level > currentArr.length) return 'never found';

  for (let i = 0; i < currentArr.length; i++) {
    if (currentArr[i].value == loneChild.parentId) {
      found = true;
      const formattedChild = {'label': loneChild.name, 'value': loneChild.id}
      currentArr[i].children ? currentArr[i].children.push(formattedChild) : currentArr[i].children = [formattedChild]
      nodeSearch(currentArr, loneChild, true);
    } else if (currentArr[i].children){
      return nodeSearch(currentArr[i].children, loneChild, false, 0)
    }
  }
  return found ? currentArr : nodeSearch(currentArr, loneChild, false, ++level);
  
}

for (let i = 0; i < data.length; i++) {
  let each_obj = data[i];
  let new_obj = {'label': each_obj.name, 'value': each_obj.id};
  if (!each_obj.parentId) { //has no parent
    formatted_data.push(new_obj) //add to final obj
  } else if (each_obj.parentName && JSON.stringify(formatted_data).indexOf(each_obj.parentName) == -1 ) { //has parent, but parent not yet listed 
    let new_parent_obj = {'label': each_obj.parentName, 'value': each_obj.parentId, 'children':[new_obj]} //create new parent with new child
    formatted_data.push(new_parent_obj) //add new parent + new child to final obj
  } else if (each_obj.parentName && JSON.stringify(formatted_data).indexOf(each_obj.parentName) > -1) { //has parent, and parent already listed _somewhere_
      nodeSearch(formatted_data, each_obj);
  }
}


return formatted_data;

3 Likes

Thanks Victoria - I'm not quite there with this but a heck of a lot closer. I'll keep trying!

Keep me posted! It's definitely a doozy :sweat_smile: