Documentation Home

Methods

Contains

bool Contains(Func<T, bool> condition)

Returns true if the tree contains AT LEAST ONE node that satisfied the condition provided by the lambda function.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male
        Id: Dad, Name: Richard Jr, Gender: Male
            Id: SisterJane, Name: Jane, Gender: Female
            Id: Me, Name: MeMyselfAndI, Gender: Male
        Id: UndleJohn, Name: John, Gender: Male
            Id: CousinAnn, Name: Ann, Gender: Female
*/

var containsFemale = familyTree.Contains(p => p.Gender == Gender.Female); // true
var containsOnlyChild = familyTree.Contains(p => p.Children.Count == 1); // true
var containsMother = familyTree.Contains(p => p.HasChildren && p.Gender == Gender.Female); // false

DeepCopy

T DeepCopy()

Makes a deep copy (clone) of the tree. If this method is called on a node that is in the middle of an existing tree, then a copy of the branch will be returned where the branch node becomes the root object. It works like an unmount in a Linux file system except that the unmounted branch is returned. Another way to look at it is that it functions like the spread operator (…) in Javascript, except that it is a recursive spread.

Warning

DeepCopy() serializes and deserializes the tree in order to create a full copy of every object. If your class contains references to anything that references it back then you will see a circular reference exception. Before calling DeepCopy(), you must ensure that there are no circular referencing properties in your class.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

// Make a copy of the whole tree
var familyTreeCopy = familyTree.DeepCopy();

// Make a copy of a branch
var branchCopy = familyTree.Children[0].DeepCopy();

/*
    branchCopy looks like this

    Id: Dad, Name: Richard Jr, Gender: Male, ParentId: null
        Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
        Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
*/

Filter

T Filter(Func<T, bool> filter)

Returns a pruned tree where only the nodes that match the filter are returned with their complete ancestor paths up to the root. Nodes that are not matched are not included. If a branch of nodes does not have a matching node in it, then the entire branch of nodes will be excluded from the returned tree.

Warning

Filter() uses the DeepCopy() method, which serializes and deserializes the tree in order to create a full copy of the objects it needs to produce the result. If your class contains references to anything that references it back then you will see a circular reference exception. Before calling DeepCopy(), you must ensure that there are no circular referencing properties in your class.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinSteve, Name: Steve, Gender: Male, ParentId: John
        Id: UncleLuke, Name: Luke, Gender: Male, ParentId: Grandpa
            Id: CousinMary, Name: Mary, Gender: Female, ParentId: UncleLuke
*/

var femaleTree = familyTree.Filter(p => p.Gender === Gender.Female);

/*
    femaleTree will look like this

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
        Id: UncleLuke, Name: Luke, Gender: Male, ParentId: Grandpa
            Id: CousinMary, Name: Mary, Gender: Female, ParentId: UncleLuke
*/

In the example above note the following:

  • Grandpa, Dad, and UncleLuke are all included because they have a matching node in their descendants

  • MeMyselfAndI is not included in the list of Children for Dad because it doesn’t match the filter condition

  • UndleJohn and its descendants are entirely excluded because there are no matching nodes in that branch

FindById

T FindById(TId nodeId)

Finds and returns the T object where the Id value is equal to the nodeId value provided as an argument. It will search the tree recursively until it is found. TId is the type of the Id property.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

var targetNode = familyTree.FindById("UndleJohn");

In the example above note the following:

  • targetNode will contain the “UncleJohn” branch in the tree above

  • targetNode.Parent.Id is “Grandpa”

  • targetNode.Children[0].Id is “CousinAnn”

Find

T Find(Func<T, bool> condition)

Finds and returns the object of type T where the condition implemented by the lambda function returns true. It will search the tree recursively until the first item is found ignoring any other nodes that match the condition.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

var targetNode = familyTree.Find(p => p.Gender == Gender.Female);

In the example above note the following:

  • targetNode.Id is “SisterJane”

  • The node with Id == "CousinAnn" also satisfies the filter condition but it is ignored because it will find “SisterJane” first

IsAncestor

bool IsAncestor(T treeNode)

Determines if the node provided as the treeNode is an ancestor of this node up the tree.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

var grandpa familyTree.FindById("Grandpa");
var dad = familyTree.FindById("Dad");
var uncle = familyTree.FindById("UncleJohn");
var me = familyTree.FindById("Me");

bool result;

// result == true
result = me.IsAncestor(grandpa);

// result == true
result = me.IsAncestor(dad);

// result == false
result = me.IsAncestor(uncle);

IsDescendent

bool IsDescendent(T treeNode)

Determines if the node provided as the treeNode is a descendant of this node down the tree recursively.

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

var grandpa familyTree.FindById("Grandpa");
var dad = familyTree.FindById("Dad");
var uncle = familyTree.FindById("UncleJohn");
var me = familyTree.FindById("Me");

bool result;

// result == true
result = grandpa.IsDescendent(dad);

// result == true
result = grandpa.IsDescendent(me);

// result == false
result = uncle.IsDescendent(me);

IsSibling

bool IsSibling(T treeNode)

Determines if the node provided as the treeNode is a sibling of this node (i.e. they share the same Parent and are members of the same Children collection).

Example:

/*
    Given the tree below

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

var dad = familyTree.FindById("Dad");
var uncle = familyTree.FindById("UncleJohn");
var sister = familyTree.FindById("SisterJane");
var me = familyTree.FindById("Me");
var cousin = familyTree.FindById("CousinAnn");

bool result;

// result == true
result = dad.IsSibling(uncle);

// result == true
result = me.IsSibling(sister);

// result == false
result = me.IsSibling(cousin);

MakeChildOfUpperSibling

void MakeChildOfUpperSibling()

Makes this node a child of the sibling higher in the order of the Children collection of its parent. In a user-interface this method could be wired to a right-arrow that moves the node to the right in the tree causing it to be indented under the item above it.

Example:

/*
    Given the tree below: CousinAnn is misplaced as a child of Grandpa

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
        Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

var cousin = familyTree.FindById("CousinAnn");

cousin.MakeChildOfUpperSibling();

/*
    The tree now looks like this

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

MakeSiblingOfParent

void MakeSiblingOfParent(bool takeLowerSiblingsAsChildren = false)

Makes this node a sibling of its parent. In a user-interface this method could be wired to a left-arrow that moves the node to the left in the tree causing it to be indented at the same level of the item above it and thereby converting it into a sibling of its parent. The takeLowerSiblingsAsChildren, if true, will cause any existing siblings under it to be converted into its children. The default is for the lower siblings to remain children of the upper (formerly Parent) item.

Example:

/*
    Given the tree below: UndleJohn is misplaced as a child of Dad

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
            Id: UndleJohn, Name: John, Gender: Male, ParentId: Dad
            Id: SisterMary, Name: Mary, Gender: Female, ParentId: Dad
*/

var uncle = familyTree.FindById("UndleJohn");

uncle.MakeSiblingOfParent();

/*
    The tree now looks like this

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
            Id: SisterMary, Name: Mary, Gender: Female, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
*/

Note the following in the example above

  • SisterMary remains a child of Dad

Example using takeLowerSiblingsAsChildren = true:

/*
    Given the tree below: UndleJohn is misplaced as a child of Dad

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
            Id: UndleJohn, Name: John, Gender: Male, ParentId: Dad
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Dad
*/

var uncle = familyTree.FindById("UndleJohn");

uncle.MakeSiblingOfParent(true);

/*
    The tree now looks like this

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: John
*/

Note the following in the example above

  • CousinAnn becomes a child of UndleJohn

MoveDownInSiblingOrder

void MoveDownInSiblingOrder()

Moves this node down in the sibling order. In a user-interface this method could be wired to a down-arrow causing the node to be moved down in the sibling order. It will stop moving down when it gets to the bottom of the sibling order at its current level in the tree.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

var sister = familyTree.FindById("SisterJane");

sister.MoveDownInSiblingOrder();

 /*
    The tree now looks like this

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

MoveUpInSiblingOrder

void MoveUpInSiblingOrder()

Moves this node up in the sibling order. In a user-interface this method could be wired to an up-arrow causing the node to be moved up in the sibling order. It will stop moving up when it gets to the top of the sibling order at its current level in the tree remaining under its current parent.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

var me = familyTree.FindById("Me");

me.MoveUpInSiblingOrder();

 /*
    The tree now looks like this

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

ProcessTree

void ProcessTree(Action<T> nodeProcessor)

Passes each child of this node to the nodeProcessor recursively down the tree, where the nodeProcessor is a lambda function used as the handler. Unlike ProcessChildren, this method will start with (include) this node.

void ProcessTree(ITreeNodeProcessor<TId, T> nodeProcessor)

Passes each child of this node to the nodeProcessor recursively down the tree, where the nodeProcessor is a handler object that implements ITreeNodeProcessor<TId, T>. Unlike ProcessChildren, this method will start with (include) this node.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

familyTree.ProcessTree(p => Console.WriteLine(p.IndentString + p.Id));

/*
    Output:

    Grandpa
     Dad
      SisterJane
      Me
     UncleJohn
      CousinAnn
*/

Note the following in the example above:

  • The root node with Id=”Grandpa” is included in the output because processing begins with the calling node itself

  • To process nodes down the tree excluding the current node use the ProcessChildren() method below

ProcessChildren

void ProcessChildren(Action<T> nodeProcessor)

Passes each child of this node to the nodeProcessor recursively down the tree, where the nodeProcessor is a lambda function used as the handler. The method does not include this node when processing the tree.

void ProcessChildren(ITreeNodeProcessor<TId, T> nodeProcessor)

Passes each child of this node to the nodeProcessor recursively down the tree, where the nodeProcessor is a handler object that implements ITreeNodeProcessor<TId, T>. The method does not include this node when processing the tree.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

familyTree.ProcessChildren(p => Console.WriteLine(p.IndentString + p.Id));

/*
    Output:

     Dad
      SisterJane
      Me
     UncleJohn
      CousinAnn
*/

Note the following in the example above:

  • The root node with Id=”Grandpa” is not in the output because processing begins with the Children

ProcessAncestors

Using Lambda with maxLevel Parameter

void ProcessAncestors(
    Action<T> nodeProcessor,
    T startNode,
    int maxLevel = 1
)

Passes each node up the tree of ancestors to the nodeProcessor function starting with startNode and stopping at the node where DepthFromRoot = 1 (i.e. the top) or the specified maxLevel.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

var me = familyTree.FindById("Me");

familyTree.ProcessAncestors(p => Console.WriteLine(p.Id), me);

/*
    Output:

    Me
    Dad
    Grandpa
*/

familyTree.ProcessAncestors(p => Console.WriteLine(p.Id), me, 2);

/*
    Output:

    Me
    Dad
*/

Note the following in the example above:

  • In both calls the starting node is me so the Id “Me” is output first

  • In the first call to ProcessAncestors() the default maxLevel = 1 was used, which means that by default it will go all the way to the root node. That is why Grandpa is in the output

  • In the second call to ProcessAncestors() it uses maxLevel = 2, so it stops processing at the ancestor with DepthFromRoot == 2, which is Dad

Using Lambda with Stop Function

void ProcessAncestors(
    Action<T> nodeProcessor,
    T startNode,
    Func<T, bool> stopFunction
)

Passes each node up the tree of ancestors to the nodeProcessor function starting with startNode and stopping at the node that causes the stopFunction to return true.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

var me = familyTree.FindById("Me");

familyTree.ProcessAncestors(
    nodeProcessor: p => Console.WriteLine(p.Id),
    startNode: me,
    stopFunction: p => p.Name.Contains("Jr")
);

/*
    Output:

    Me
    Dad
*/

Note the following in the example above:

  • In both calls the starting node is me so the Id “Me” is output first

  • The stop function is more complex than a level number

  • You can make the stop function as sophisticated as is necessary

Using ITreeNodeProcessor Class with maxLevel

void ProcessAncestors(
    ITreeNodeProcessor<TId, T> nodeProcessor,
    T startNode,
    int maxLevel = 1
)

Passes each node up the tree of ancestors to the nodeProcessor starting with startNode and stopping at the node where DepthFromRoot = 1 (i.e. the top) or the specified maxLevel.

Example:

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

public class MyTreeNodeProcessor : ITreeNodeProcessor<string, Person>
{
    public void ProcessNode(Person node) => Console.WriteLine(node.Id);
}

var myNodeProcessor = new MyTreeNodeProcessor();

var me = familyTree.FindById("Me");

familyTree.ProcessAncestors( myNodeProcessor, me );

/*
    Output:

    Me
    Dad
    Grandpa
*/

Note the following in the example above:

  • In both calls the starting node is me so the Id “Me” is output first

  • The default maxLevel = 1 was used, which means that by default it will go all the way to the root node, which is why Grandpa is in the output

  • While not demonstrated here, when using a class you can also specify an integer maxLevel or a stopFunction lambda to make it stop climbing the ancestor tree at the defined level

ToList

List<T> ToList()

Returns all nodes in the tree as a flattened collection of type List<T> sorted by the SortableTreePath property. If this method is called on a branch then it will return only the nodes in that branch where the top of the branch is the root and will be the first element in the list.

/*
    Given the tree below:

    Id: Grandpa, Name: Richard, Gender: Male, ParentId: null
        Id: Dad, Name: Richard Jr, Gender: Male, ParentId: Grandpa
            Id: SisterJane, Name: Jane, Gender: Female, ParentId: Dad
            Id: Me, Name: MeMyselfAndI, Gender: Male, ParentId: Dad
        Id: UndleJohn, Name: John, Gender: Male, ParentId: Grandpa
            Id: CousinAnn, Name: Ann, Gender: Female, ParentId: Grandpa
*/

var flatList = familyTree.ToList();

flatList.ForEach(p => p.Console.WriteLine(p.SortableTreePath));

/*
    Output:

    1
    1.1
    1.1.1
    1.1.2
    1.2
    1.2.1
*/

flatList.ForEach(p => p.Console.WriteLine(p.IndentString + p.Id));

 /*
    Output:

    Grandpa
     Dad
      SisterJane
      Me
     UncleJohn
      CousinAnn
*/

Note the following in the example above:

  • The first call above shows how the SortableTreePath property can be used to display the tree items in the property order without having to recurse the tree even through the list is flat.

  • The second call above demonstrates the fact that the tree can be displayed in nested fashion using the IndentString property without recursing by virtue of the fact that the list is sorted by the SortableTreePath property.