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.
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).
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>
+