Not yet rated

Problem

An efficient way to handle hierarchal data is through a flat structure that details parent relationship. However, iterating over and visualizing this data can require difficult to understand looping or recursion.

Solution

Instead of multiple pass parsing of the data by starting at the root node and working to the extremities, one can explode the data into a complete structure in a single pass by working from extremities towards the node. This can be accomplished in a single pass as every node has an indeterminate number of children, but only one parent (except the root that has zero).

Detailed explanation

Assume the data is in an associative array such as the following:

<cfscript>
flatTable = StructNew();

flatTable["1"] = "root";
flatTable["2"] = "1";
flatTable["3"] = "1";
flatTable["4"] = "1";
flatTable["5"] = "2";
flatTable["6"] = "5";
flatTable["7"] = "3";
flatTable["8"] = "3";
</cfscript>

The first thing to do is to translate each flatTable entry into a structure that contains members for tracking parent and children.

<cfscript>
_working = StructNew();

// Have to create a placeholder for the Root node
_working["root"] = StructNew();
_working["root"].children = ArrayNew(1);
   
for (key in arguments.flatTable) {
     _working["#key#"] = newNode(key,tree[key]);
}
</cfscript>

The newNode function is defined as:

<cffunction name="newNode" returntype="Struct" >
     <cfargument name="key" type="string" required="true" />
     <cfargument name="value" type="string" required="true" />
  
     <cfscript>
     var tempStruct = StructNew();
     tempStruct.label = arguments.key; // key
     tempStruct.parent = arguments.value; // value
     tempStruct.children = ArrayNew(1);      
     return tempStruct;
     </cfscript>
</cffunction>


Now we can sort the structure to ensure that we can walk the hierarchy:

<cfscript>
sortedStruct = StructSort(arguments.flatTable,'textnocase');
</cfscript>

Since we have an ordered strcture, we now can complete the job by iterating over the struct in reverse:

<cfscript>
for (i=ArrayLen(sortedStruct); i>0; i--) {
     key = sortedStruct[i];
     parent = arguments.flatTable["#sortedStruct[i]#"];
  
     _working["#parent#"].children[ArrayLen(_working["#parent#"].children)+1] = _working["#key#"];
      
}   
</cfscript>

This can be abstracted into a single function reference (along with the newNode function):

<cffunction name="explodedView" returntype="struct">
     <cfargument name="flatTable" type="struct" required="true" />
     <cfset var _working = StructNew() />
     <cfset var sortedStruct = StructNew() />
   
     <cfscript>
     // create the root level node
     _working["root"] = StructNew();
     _working["root"].children = ArrayNew(1);
   
     for (key in arguments.flatTable) {
          _working["#key#"] = newNode(key,arguments.flatTable[key]);
     }
   
     sortedStruct = StructSort(arguments.flatTable,'textnocase');

     for (i=ArrayLen(sortedStruct); i>0; i--) {
          key = sortedStruct[i];
          parent = arguments.flatTable["#sortedStruct[i]#"];
      
          _working["#parent#"].children[ArrayLen(_working["#parent#"].children)+1] = _working["#key#"];
          
     }   
    
     return _working["root"];
     </cfscript>  
</cffunction>
 


+
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.

Report abuse

Related recipes