recursive array loop?
BlitzMax Forums/BlitzMax Beginners Area/recursive array loop?
hi,
i am trying to make a recursive function that loops through a multidimensional array, i've found this PHP code
Function loop($array, $parent=''){
foreach( $array as $option => $value ){
If( $parent != '' ){
$option = $parent . '[' . $option . ']';
}
If( is_array($value) ){
loop($value, $option);
} Else {
?>
<div><?php echo $option; ?> = <?php echo $value; ?></div>
<?php
}
}
}
loop ( $names );
i am still learning the blitzmax syntax and do not know php very well, so i am struggling with porting the php idea to blitzmax...is it even possible? or is the array handling in blitzmax too different?
Why do you need recursion? Don't you know how many dimensiosn your array has, so you can just use nested for-next loops?
A problem I can see is that BMax requires you to know the type of array, the arrays in PHP can hold variable values.
You *could* do it with objects I suppose, but I've no doubt there'll be problems doing it.
"or is the array handling in blitzmax too different?"
Short answer: yes. If you describe exactly what it is you're trying to do, there might be alternative methods.
in short, what i am trying to achieve is to copy a xml structure to a treeview, and use the treeview to draw objects from attributes in the xml.
i got it working, but the code is nasty, i was thinking that if i could nail that recursive loop, i'd learn some ways of array handling that would clean up my code a bit...
my code at the moment populating a level 5 array:
TL.level[levelCnt].obj[objCnt].subObj1[subObj1Cnt].subObj2[subObj2Cnt].xObj = TL.level[levelCnt].obj[objCnt].subObj1[subObj1Cnt].subObj2[subObj2Cnt].xObj[..xObjCnt+1]
TL.level[levelCnt].obj[objCnt].subObj1[subObj1Cnt].subObj2[subObj2Cnt].xObj[xObjCnt] = node
...please let me know if anyone has any alternative methods?
funny enough Im doing something similar.
Im not at my home PC right now so its from memory but in a nutshell I have all my data in a TMap
a TMap has two parameters so the key is the field name and the value is the value stored as a string value OR another TMap.
In the procedure to read the TMap
Procedure processData(inData:TMap)
it cycles through the keys in inData.
For readkey:string = eachin inData.keys
You can then see if the value if the key is another TMap.
If TMap(getvaluefromkey(inData,readkey)) Then
' Call myself passing the TMap value
processData(TMap(getvaluefromkey(inData,readkey)))
Else
print inkey + " = " + String(getvaluefromkey(inData,readkey)
The actual code syntax is probably all wrong Im working from memory but thats roughly it.
When I get time I was going to change it to just use some sort of TList as a stack so instead of recursive calling I push/pop my state using the stack.
ah, the TMap returns. i had a feeling it would.
Thanks for the TMap info, i think i get where you are going.
will read up on hashtables and see if can understand better the difference between TLists and TMaps...
umm, ok, if i can assume that a TMap works like a hash table, TMaps sounds like a better idea than TLists as they like arrays, use a direct lookup thereby being faster.
is this correct, or am i completely off the rail here?
I think It is much more efficient to implement complex tree structures (like xml structures) using your own object design. something like:
Type MyNode
Field Childs:TList = New TList
Method AddChild(Node:MyNode)
if node <> null then
childs.addlast(Node)
else
throw "Null nodes are not allowed"
endif
End Method
End Type
Local Root:MyNode = New MyNode
You can get all the nodes in the root. You can get any subnode of any node. the same as a tree control. Obviously you may have to implement the CountNodes, GetNodeAtIndex, RemoveNode, etc... but it should be very easy.
mmhm, that is not too far from what i've got already...
so many ways to achieve this - don't know which one to go for...
my newbie head is also thinking of a single TList/TMap of types with parentID fields, so that i could retrieve data with matching parentID? sounds like an easy, but maybe not very efficient way....
for local node:myNode = eachin nodelist
if (node.parentID = 20)
print node.name
endif
next
You will have to deal with some 'seek Id' 'comapre ID' etc for every node. why don't you think of nodes as real objects? I mean, store any child node in its parent node.
you can do:
Type MyNode
Field Childs:TList = New TList
Field Name:String
Method AddNode:MyNode(Node:MyNode)
if node <> null then
childs.addlast(Node)
return Node
else
throw "Null nodes are not allowed"
endif
End Method
Method CountNodes:int()
return Childs.Count()
End Method
Method NodeAtIndex:MyNode(index:int)
return childs.valueAtIndex(index)
End Method
End Type
'Create the root node:
Local Root:MyNode = New MyNode
'Create tree nodes in the root:
Local AuxNode:MyNode
AuxNode = New MyNode
AuxNode.Name = "First Child Node"
Root.AddNode(AuxNode)
AuxNode = New MyNode
AuxNode.Name = "Second Child Node"
Root.AddNode(AuxNode)
AuxNode = New MyNode
AuxNode.Name = "Third Child Node"
Root.AddNode(AuxNode)
'Now we add a subnode to the second node in the root.
AuxNode = root.NodeAtIndex(1) 'We get the second node of the root (the first one is index 0)
AuxNode.AddNode(New MyNode) 'We add it a child node
AuxNode.NodeAtIndex(0).Name = "This is a subnode!" 'We set the child node a name
this is a very fast and flexible way to create a complex tree.
I haven't tested it, becouse I'm not at my computer now, so it can be buggy.
I have a better example, it also produces recursive execution.
Type MyNode
Global PathSeparator:String = "/"
Field Childs:TList = New TList
Field Name:String
Field Parent:MyNode
Method AddNode:MyNode(Node:MyNode)
If node <> Null Then
childs.addlast(Node)
Node.Parent = Self
Return Node
Else
Throw "Null nodes are not allowed"
EndIf
End Method
Method CountNodes:Int()
Return Childs.Count()
End Method
Method NodeAtIndex:MyNode(index:Int)
Return MyNode(childs.valueAtIndex(index))
End Method
Method RemoveNode(Node:MyNode)
Node.Parent = Null
Self.childs.Remove(Node)
End Method
Method NodePath:String()
If parent = Null Then Return Name
Return parent.NodePath() + Self.PathSeparator + Self.name
End Method
'Summary: This method takes a function and calls that function with every existing node as a parameter.<br>The function has to have the same signature (param1:MyNode)
Method RecursiveCall(functionnode(node:MyNode))
functionnode(Self)
For Local node:MyNode = EachIn childs
Node.RecursiveCall(functionnode)
Next
End Method
End Type
'Create the root node:
Local Root:MyNode = New MyNode
root.Name = "RootNode"
'Create tree nodes in the root:
Local AuxNode:MyNode
AuxNode = New MyNode
AuxNode.Name = "First Child Node"
Root.AddNode(AuxNode)
AuxNode = New MyNode
AuxNode.Name = "Second Child Node"
Root.AddNode(AuxNode)
AuxNode = New MyNode
AuxNode.Name = "Third Child Node"
Root.AddNode(AuxNode)
'Now we add a subnode to the second node in the root.
AuxNode = root.NodeAtIndex(1) 'We get the second node of the root (the first one is index 0)
AuxNode.AddNode(New MyNode) 'We add it a child node
AuxNode.NodeAtIndex(0).Name = "This is a subnode!" 'We set the child node a name
'now we make a recursove call
Root.recursiveCall PrintPath 'We send the function PrintPath as a parameter. This function will be called recursivelly for every node.
Function PrintPath(Node:MyNode)
Print "Node path: " + node.NodePath()
End Function
This example generates a little cross-referencing issue, so be sure to call RemoveNode if you need to delete a node from its parent childs list. If you don't do this, the object will remain in memory, ignored by the GC
putting a addNode method in the type, seems like a good idea - i think that will clean up my code a lot.
and now if i can find a clever way to jump backwards as my recursive leafs are dying i think i am all set.
maybe if i keep the level history in a array or something, i'll be able to jump from an outer leaf all the way back to root, hmmm
ah, beautiful ziggy! i just saw your last post - recursive execution, that sounds like exactly what i need!
thanks a lot ziggy, i have to rush off now, but i will have a look at your code in the morning, and see what i can make out of it - it looks very promising, thanks!