Generic wrapper for LINQ to Tree

In this post I dealt with a  simple extension function to flatten a tree and in one of the comment Robert shared an interesting link that deal with the creation of a wrapper structure to use LINQ style function on tree structure. That article is really interesting, but the adopted solution requires to create a wrapper for every structure you need to iterate into and I decided to spend a couple of minutes to verify how difficult is writing a generic solution.

Thanks to Func<T> is quite easy to write a wrapper that does not relay on code generation to wrap a specific tree type.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public class TreeWrapper<T> where T : class
{
private readonly T _node;
private readonly Func<T, IEnumerable<T>> _getChildernFunc;
private readonly Func<T, T> _getParentFunc;
 
public TreeWrapper(
T node,
Func<T, IEnumerable<T>> getChildernFunc,
Func<T, T> getParentFunc)
{
_node = node;
_getChildernFunc = getChildernFunc;
_getParentFunc = getParentFunc;
}

As you can see I created a wrapper that takes a node, and a couple of functions, one to find all child of the element, and the other to find the parent of the node. With this simple wrapper writing a Descendants() function is supereasy.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
public IEnumerable<T> Descendants()
{
return Descendants(_getChildernFunc(_node));
}
 
private IEnumerable<T> Descendants(IEnumerable<T> elements)
{
return elements.Concat(
elements.SelectMany(e => Descendants(_getChildernFunc(e))));
}

Et voilà , this simple method makes this test pass.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
[Test]
public void VerifyGenericTreeNodeDescendants()
{
GenericTreeNode r1 = new GenericTreeNode(1,
new GenericTreeNode(2,
new GenericTreeNode(4)),
new GenericTreeNode(3),
new GenericTreeNode(5,
new GenericTreeNode(6), new GenericTreeNode(7)));
 
var w = TreeWrapper.Create(r1, n => n.GenericTreeNodes, n => n.Parent);
w.Descendants().Select(e => e.Id)
.Should().Have.SameSequenceAs(new int[] {2, 3, 5, 4, 6, 7});
}

The GenericTreeNode class is used only for testing purpose and is a simple class that maintains a reference to parent and a collection of childs. The ancestors function is even simplier.

1
2
3
4
5
6
7
8
9
public IEnumerable<T> Ancestors()
{
T parent = _getParentFunc(_node);
while (parent != null)
{
yield return parent;
parent = _getParentFunc(parent);
}
}

Simply iterate to all parents until the parent is null (reached the root node), but we can still add more interesting methods.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
public IEnumerable<T> ElementBeforeSelf()
{
T parent = _getParentFunc(_node);
if (parent != null)
{
return _getChildernFunc(parent)
.TakeWhile(e => e != _node);
}
return new T[] {};
}
 
public IEnumerable<T> ElementAfterSelf()
{
T parent = _getParentFunc(_node);
if (parent != null)
{
return _getChildernFunc(parent)
.SkipWhile(e => e != _node)
.Skip(1);
}
return new T[] { };
}

ElementsBeforeSelft() and ElementsAfterSelf() return all sibling elements (node at same level) before and after the current node element, this makes this test pass.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
[Test]
public void VerifyGenericTreeElementBeforeSelf()
{
GenericTreeNode node;
new GenericTreeNode(1,
new GenericTreeNode(2,
new GenericTreeNode(4)),
new GenericTreeNode(3),
node = new GenericTreeNode(5,
new GenericTreeNode(6), new GenericTreeNode(7)),
new GenericTreeNode(8),
new GenericTreeNode(9));
 
var w = TreeWrapper.Create(node, n => n.GenericTreeNodes, n => n.Parent);
w.ElementBeforeSelf().Select(e => e.Id)
.Should().Have.SameSequenceAs(new int[] { 2, 3 });
 
w.ElementAfterSelf().Select(e => e.Id)
.Should().Have.SameSequenceAs(new int[] { 8, 9 });
}

The only disadvantage of this technique, is that I’m not able to write something like this.

1
2
3
w.Descendants().Single(e => e.Id == 5)
.ElementBeforeSelf().Select(e => e.Id)
.Should().Have.SameSequenceAs(new int[] { 2, 3 });

This query simple select a single element and then iterates on all the element before self, but it does not compile, because the Descendants() method returns an IEnumerable<T> (T is the wrapped object) and not a IEnumerable<TreeWrapper<T>>. You could modify the Descendants() function to wrap each returned element, but this is not optimal, so I prefer a simply extension method to rewrap an element, that permits me to write this test.

1
2
3
4
w.Descendants().Single(e => e.Id == 5)
.TreeWrap(n => n.GenericTreeNodes, n => n.Parent)
.ElementBeforeSelf().Select(e => e.Id)
.Should().Have.SameSequenceAs(new int[] { 2, 3 });

This syntax is not bad, the Single LINQ operator permits me to iterate in all Descendants node of the wrapped node, then I need to rewrap again the result to use the ElementBeforeSelf method to use again a function of the TreeWrapper class.

Alk.