Recursively search nested lists; Returning parent
This is a follow up post to this one: Recursively search nested lists
I've been attempting to modify the code that resulted to allow me to return a parent item of whatever child is located, but the recursive aspect of this function doesn't flow in my brain very well for some reason.
In a perfect world I'd like to be able to specify how many parent nodes up to return, but even just the immediate would be great.
public static AccessibleTreeItem Find(AccessibleTreeItem node, string name)
{
if (node == null)
return null;
if (node.name == name)
retur开发者_开发知识库n node;
foreach (var child in node.children)
{
var found = Find(child, name);
if (found != null)
return found;
}
return null;
}
Thanks all for taking the time to have a look.
Edit: So, for example, I'd like to be able to call Find(node, name, parentIndex) or something similar, to pass through a value that represents the number of parent levels the item being returned can be found at.
Edit 2: To clarify, I would like to be able to tell the function to find a particular name, and a parentIndex. The function would locate the node being searched for, and then traverse up the 'tree' the specified number of levels, and finally returning that object.
So if we called
Find(node, "Test", 2)
And the value being searched for was at:
node.children[0].children[3].children[2].children[1]
Then the function should return the object located at:
node.children[0].children[3]
Aha... so try passing down byref a (positive) integer called "ancestorCount" (1 for parent, 2 for grandparent, 3 for great-gran-dad, etc, etc)... if found returns true (not null) and the ancestorCount==0 return this, othewise decrement the ancestorCount before returning the found child...
Does that make sense to you? I'll edit this post in about 10 minutes to put that into psuedocode (at least).
Cheers. Keith.
EDIT: To provide sort-of code at-least.
This isn't compiled, let-alone tested... and therefore I'm not at all certain that I've got the condition correct... As Ben pointed out, I might have it completely assbackwards ;-) This is kind of thing that I typically have to debug for a while to get right... but I do think "I'm pretty close", and the core idea is there... I'll leave the fine-tuning upto the implementor... you ;-)
public static Node Find(Node node, string targetName, ref int ancenstorCount) {
if ( node == null )
return null;
if ( ancenstorCount == 0 ) // we've already found the right level to return
return node;
if ( node.name == targetName )
return node;
foreach ( Node child in node.Children ) {
var found = Find(child, targetName);
if ( found != null ) {
if ( ancenstorCount == 0 )
return found;
--ancenstorCount;
return node; // return any non-null
}
}
return null;
}
... But I can see that I'm going to have to implement Node now, just to satisfy my own curiosity about the correctness of my algorith. Sigh.
Cheers. Keith.
EDIT2:
Well, this works for me...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Tree
{
class Node {
private List<Node> _kids = new List<Node>();
public string Name { get; set; }
public List<Node> Children { get { return _kids; } }
public void Add(Node child) { _kids.Add(child); }
public override string ToString() { return Name; }
}
class Program
{
Node _root;
static void Main(string[] args) {
new Program().Run();
Console.Write("Press any key ...");
Console.ReadKey();
}
Program() {
_root = GenerateTree();
}
static Node GenerateTree()
{
// Gen0
Node root = new Node() { Name = "Great-grandpa Jimmy Joe Jim-Bob John Keith Luke Duke" };
// Add Gen1 to Gen0
root.Add(new Node() { Name = "GrandUncle Joe Duke" });
Node grandDad = new Node() { Name = "Granddad Jimmy Duke" };
root.Add(grandDad);
// Add Gen2 to Gen1
grandDad.Add(new Node() { Name = "Uncle Jim" });
grandDad.Add(new Node() { Name = "Uncle Bob" });
Node dad = new Node() { Name = "Dad John" };
grandDad.Add(dad);
// Add Gen3 to Gen2
dad.Add(new Node() { Name = "Brother Luke" });
dad.Add(new Node() { Name = "Keith" });
return root;
}
void Run() {
Console.WriteLine("My Great-granddad is: " + Find("Keith", 3));
Console.WriteLine("My granddad is: " + Find("Keith", 2));
Console.WriteLine("My dad is: " + Find("Keith", 1));
Console.WriteLine();
Console.WriteLine("Brother Luke's Dad is: " + Find("Brother Luke", 1));
Console.WriteLine("Uncle Bob's dad is: " + Find("Uncle Bob", 1));
Console.WriteLine("Uncle Jim's granddad is: " + Find("Uncle Jim", 2));
Console.WriteLine();
Console.WriteLine("Lunil's mom is: " + Find("Lunil", 1));
Console.WriteLine();
}
string Find(string targetName, int anticedentLevel) {
Node n = Find(_root, targetName, ref anticedentLevel);
return n!=null ? n.ToString() : "#NOT_FOUND#";
}
private static Node Find(Node node, string targetName, ref int ancenstorCount) {
if ( node == null )
return null;
if ( ancenstorCount == 0 ) // we've already found the right level to return
return node;
if ( node.Name == targetName )
return node;
foreach ( Node child in node.Children ) {
var found = Find(child, targetName, ref ancenstorCount);
if ( found != null ) {
if ( ancenstorCount == 0 )
return found;
--ancenstorCount;
return node; // return any non-null
}
}
return null;
}
}
}
OUTPUT
My Great-granddad is: Great-grandpa Jimmy Joe Jim-Bob John Keith Luke Duke My granddad is: Granddad Jimmy Duke My dad is: Dad John Brother Luke's Dad is: Dad John Uncle Bob's dad is: Granddad Jimmy Duke Uncle Jim's granddad is: Great-grandpa Jimmy Joe Jim-Bob John Keith Luke Duke Lunil's mom is: #NOT_FOUND# Press any key ...
... though in the real world you'd probably "just" implement it with a parent reference in each node, simply because that's simpler... and useful for other things.
I don't know what "AccessibleTreeItem" is - but IF it has a parent property, then it's an easy matter of looping to node.Parent.Parent.
for instance:
public AccessibleTreeItem GetParent(AccessibleTreeItem node, int depth)
{
if(depth <= 1) // can't go higher than 1
{
return node.Parent;
}
else
{
return GetParent(node.Parent, depth-1);
}
}
So then from your existing recursion, instead of returning node, you return GetParent(node, depth)
If you DON'T have a parent reference, however, then you need to get a bit more creative. I see that corlettk is working on that example and it sounds like he's doing what I'd do, so I'll leave that to him.
精彩评论