/// <summary> /// Gets a node's leaves, i.e. all descendants of that node that do not have children. If /// the node has no children then the node itself is returned. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node"> /// The node whose leaves are to be returned. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the node's /// leaves. /// </returns> public static IEnumerable <T> GetLeaves <T>(this ITreeWalker <T> walker, T node) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to hold enumerators and push the child enumerator of 'node'. Stack <IEnumerator <T> > stack = new Stack <IEnumerator <T> >(); stack.Push(walker.GetChildren(node).GetEnumerator()); if (!stack.Peek().MoveNext()) { // If the enumerator on the stack has no items then 'node' is a leaf node. // Dispose of the enumerator and yield 'node' and we are done. stack.Pop().Dispose(); yield return(node); } else { // The current node is now the first child of the 'node' parameter. // Continue looping as long as the stack has enumerators on it. while (stack.Count > 0) { // Push the current node's enumerator on the stack. stack.Push(walker.GetChildren(stack.Peek().Current).GetEnumerator()); // Continue pushing enumerators on the stack as long the enumerator on the top // of the stack has items. while (stack.Peek().MoveNext()) { stack.Push(walker.GetChildren(stack.Peek().Current).GetEnumerator()); } // Once we reach an enumerator that has no items pop that enumerator and // dispose of it. stack.Pop().Dispose(); // The 'Current' property of the enumerator on the top of the stack is a leaf // node. Return it. yield return(stack.Peek().Current); // Continue popping enumerators off of the stack and disposing of them until // we reach an enumerator with another item. That item become the current // node. while (stack.Count > 0 && !stack.Peek().MoveNext()) { stack.Pop().Dispose(); } } } }
/// <summary> /// Gets a node and the node's siblings, i.e. all nodes that share the same parent. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node"> /// The node whose siblings are to be returned. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains the /// siblings. /// </returns> public static IEnumerable <T> GetSiblingsAndSelf <T>(this ITreeWalker <T> walker, T node) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Get the node's parent. T parent; if (walker.TryGetParent(node, out parent)) { // Return all of the parent's children with the exception of the original node. return (walker .GetChildren(parent)); } else { // If the node does not have a parent then return the node. return(new T[] { node }); } }
/// <summary> /// Gets a node's siblings, i.e. all nodes that share the same parent. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node"> /// The node whose siblings are to be returned. /// </param> /// <param name="comparer"> /// An <see cref="System.Collections.Generic.IEqualityComparer<T>"/> that knows how /// to compare two nodes for equality. This is used to make sure that /// <paramref name="node"/> is not returned in the resulting /// <see cref="System.Collections.Generic.IEnumerable<T>"/>. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains the /// siblings. /// </returns> public static IEnumerable <T> GetSiblings <T>(this ITreeWalker <T> walker, T node, IEqualityComparer <T> comparer) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // If the comparer is null then use the default comparer. comparer = comparer ?? EqualityComparer <T> .Default; // Get the node's parent. T parent; if (walker.TryGetParent(node, out parent)) { // Return all of the parent's children with the exception of the original node. return (walker .GetChildren(parent) .Where(x => !comparer.Equals(node, x))); } else { // If the node does not have a parent then return an empty IEnumerable. return(Enumerable.Empty <T>()); } }
/// <summary> /// Gets children based on a predicate. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node"> /// The root node to be queried. /// </param> /// <param name="predicate"> /// A predicate to test each child for selection. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// matching children. /// </returns> public static IEnumerable <T> GetChildren <T>( this ITreeWalker <T> walker, T node, Func <T, bool> predicate) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } return (walker .GetChildren(node) .Where(predicate)); }
/// <summary> /// Gets the children that match the <paramref name="key"/>. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="nodes"> /// The root nodes to be queried. /// </param> /// <param name="key"> /// The key that each child will be compared to. /// </param> /// <param name="comparer"> /// The <see cref="IEqualityComparer<T>"/> used to compare the key and the child. If /// this is null then the default <see cref="EqualityComparer<T>.Default"/> will be /// used. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// matching children. /// </returns> public static IEnumerable <T> GetChildren <T>( this ITreeWalker <T> walker, IEnumerable <T> nodes, T key, IEqualityComparer <T> comparer) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (nodes == null) { throw new ArgumentNullException("nodes"); } if (key == null) { throw new ArgumentNullException("key"); } // If the comparer is null then use the defautl comparer for that type. comparer = comparer ?? EqualityComparer <T> .Default; return (nodes .SelectMany(n => walker.GetChildren(n)) .Where(n => comparer.Equals(key, n))); }
/// <summary> /// Gets the child at the specified index or the default value of the specified type. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node">The node whose child is to be returned.</param> /// <param name="index">The index of the child to retrieve.</param> /// <returns>The child node at the specified index or the default value.</returns> /// /// <exception cref="ArgumentNullException"> /// If <paramref name="walker"/> or <paramref name="node"/> is null. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// If <paramref name="index"/> is less than zero. /// </exception> public static T GetChildAtOrDefault <T>( this ITreeWalker <T> walker, T node, int index) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } if (index < 0) { throw new ArgumentOutOfRangeException("index"); } return (walker .GetChildren(node) .ElementAtOrDefault(index)); }
/// <summary> /// Gets the child at the specified index or the default value if the index is out of /// range. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="nodes">The nodes whose children are to be returned.</param> /// <param name="index">The index of the child to retrieve.</param> /// <returns> /// The child node at the specified index or default values for each of the nodes in /// <see cref="nodes"/>. /// </returns> /// <exception cref="ArgumentNullException"> /// If <paramref name="walker"/> or <paramref name="nodes"/> is null. /// </exception> /// <exception cref="ArgumentOutOfRangeException"> /// If <paramref name="index"/> is less than zero. /// </exception> public static IEnumerable <T> GetChildAtOrDefault <T>( this ITreeWalker <T> walker, IEnumerable <T> nodes, int index) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (nodes == null) { throw new ArgumentNullException("nodes"); } if (index < 0) { throw new ArgumentOutOfRangeException("index"); } return (nodes .Select(n => walker .GetChildren(n) .ElementAtOrDefault(index))); }
/// <summary> /// Enumerates a tree using the pre-order traversal method. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node">The root node of the tree that is to be traversed.</param> /// <param name="excludeSubtreePredicate"> /// A <see cref="System.Func<T, Int32, Boolean>"/> that determines if the current node /// that is being evaluated (and all of its descendants) should be included in the /// traversal. This allows for short-circuiting of the pre-order traversal by excluding /// particular subtrees from the traversal. The first argument is the current node being /// evaluated and the second argument is the depth of the current node relative to the /// original node that the traversal began on. /// </param> /// <param name="excludeOption"> /// Used in conjunction with the <paramref name="excludeSubtreePredicate"/>. Determines /// if the entire subtree should be excluded or just its children. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// nodes in the tree ordered based on a pre-order traversal. /// </returns> public static IEnumerable <T> PreOrderTraversal <T>( this ITreeWalker <T> walker, T node, Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to keep track of the branches being traversed. Stack <IEnumerator <T> > enumerators = new Stack <IEnumerator <T> >(); enumerators.Push(Enumerable.Repeat(node, 1).GetEnumerator()); // Loop as long as we have an enumerator on the stack. When we have popped the last // one off the stack the traversal is complete. while (enumerators.Count > 0) { if (enumerators.Peek().MoveNext()) { node = enumerators.Peek().Current; // If the 'excludeSubtreePredicate' is not null and evaluates to true then // yield the current node if 'excludeOption' is set to exclude the children. // Otherwise, do not yield anything. if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(node, enumerators.Count - 1)) { if (excludeOption == ExcludeOption.ExcludeDescendants) { yield return(node); } } else { // Yield the current node then push it and its children onto the // stack. yield return(node); enumerators.Push(walker.GetChildren(node).GetEnumerator()); } } else { // Pop the enumerator and dispose of it. enumerators.Pop().Dispose(); } } }
/// <summary> /// Gets the height of the node. The height is measured by the number of edges between /// <paramref name="node"/> and the deepest leaf. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node"> /// The node whose height is to be returned. /// </param> /// <returns> /// The number of edges between <paramref name="node"/> and the deepest leaf. /// </returns> public static int GetHeight <T>(this ITreeWalker <T> walker, T node) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to hold enumerators and push the child enumerator of 'node'. Stack <IEnumerator <T> > stack = new Stack <IEnumerator <T> >(); stack.Push(walker.GetChildren(node).GetEnumerator()); // Keep track of the max height. int height = 0; // Continue looping as long as the stack has enumerators on it. // If an enumerator has an item push that item's child enumerator on the stack and // update 'height'. Otherwise, pop the enumerator off the stack and dispose of it. while (stack.Count > 0) { if (stack.Peek().MoveNext()) { stack.Push(walker.GetChildren(stack.Peek().Current).GetEnumerator()); // If the stack height (minus one because height is zero based) is greater // than the current height then update the current height. if (stack.Count - 1 > height) { height = stack.Count - 1; } } else { stack.Pop().Dispose(); } } return(height); }
/// <summary> /// Tries to get the child at the specified index. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="nodes">The nodes whose children are to be returned.</param> /// <param name="index">The index of the child to retrieve.</param> /// <param name="child"> /// The child that was found at the specified index or a default value if the index was /// out of range. /// </param> /// <returns> /// True, if a child was found at the specified index; false, otherwise. /// </returns> internal static bool TryGetChildAt <T>( this ITreeWalker <T> walker, T node, int index, out T child) { // Performance optimization. Checking to see if the result from GetChildren returns // an IList. If it does then we can simply get the child using the list's indexer // property. IList <T> list = walker.GetChildren(node) as IList <T>; if (list != null) { // If the index is within bounds then get the child an return true. if (index < list.Count) { child = list[index]; return(true); } } else { // Iterate over each child, decrementing the index while doing so. // If the index reaches 0 then we have found the child. // Get the child and return true. using (IEnumerator <T> enumerator = walker.GetChildren(node).GetEnumerator()) { while (index >= 0 && enumerator.MoveNext()) { index--; } if (index == -1) { child = enumerator.Current; return(true); } } } // If no child was found then return false. child = default(T); return(false); }
/// <summary> /// Determines if a node has children. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node"> /// The node being checked for children. /// </param> /// <returns> /// Returns a <see cref="System.Boolean"/> indicating whether or not the node has children. /// </returns> public static bool HasChildren <T>(this ITreeWalker <T> walker, T node) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } return(walker.GetChildren(node).Any()); }
/// <summary> /// Gets the degree of a node (number of children). /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node">The node whose degree is to be returned.</param> /// <returns>The degree (number of children) of the node.</returns> public static int GetDegree <T>(this ITreeWalker <T> walker, T node) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Return the number of children the node has. return(walker.GetChildren(node).Count()); }
/// <summary> /// Gets a node's following siblings, i.e. all nodes that share the same parent and follow /// the node in the parent's list of children. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node"> /// The node whose siblings are to be returned. /// </param> /// <param name="comparer"> /// An <see cref="System.Collections.Generic.IEqualityComparer<T>"/> that knows how /// to compare two nodes for equality. This is used to make sure that /// <paramref name="node"/> is not returned in the resulting /// <see cref="System.Collections.Generic.IEnumerable<T>"/>. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains the /// siblings. /// </returns> public static IEnumerable <T> GetFollowingSiblings <T>(this ITreeWalker <T> walker, T node, IEqualityComparer <T> comparer) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // If the comparer is null then use the default comparer. if (comparer == null) { comparer = EqualityComparer <T> .Default; } // Get the node's parent. T parent; if (walker.TryGetParent(node, out parent)) { // Return all of the parent's children after the original node. using (IEnumerator <T> enumerator = walker.GetChildren(parent).GetEnumerator()) { while (enumerator.MoveNext() && !comparer.Equals(enumerator.Current, node)) { ; } while (enumerator.MoveNext()) { yield return(enumerator.Current); } } } else { // If the node does not have a parent then return an empty IEnumerable. yield break; } }
/// <summary> /// Gets a node's preceding siblings, i.e. all nodes that share the same parent and precede /// the node in the parent's list of children. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node"> /// The node whose siblings are to be returned. /// </param> /// <param name="comparer"> /// An <see cref="System.Collections.Generic.IEqualityComparer<T>"/> that knows how /// to compare two nodes for equality. This is used to make sure that /// <paramref name="node"/> is not returned in the resulting /// <see cref="System.Collections.Generic.IEnumerable<T>"/>. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains the /// siblings. /// </returns> public static IEnumerable <T> GetPrecedingSiblings <T>(this ITreeWalker <T> walker, T node, IEqualityComparer <T> comparer) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // If the comparer is null then use the default comparer. if (comparer == null) { comparer = EqualityComparer <T> .Default; } // Get the node's parent. T parent; if (walker.TryGetParent(node, out parent)) { // Return all of the parent's children up to (not not including) the original node. foreach (T child in walker.GetChildren(parent)) { if (comparer.Equals(child, node)) { yield break; } else { yield return(child); } } } else { // If the node does not have a parent then return an empty IEnumerable. yield break; } }
/// <summary> /// Gets the children that match the <paramref name="key"/>. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="nodes"> /// The root nodes to be queried. /// </param> /// <param name="key"> /// The key that each child will be compared to. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// matching children. /// </returns> public static IEnumerable <T> GetChildren <T>( this ITreeWalker <T> walker, IEnumerable <T> nodes, T key) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (nodes == null) { throw new ArgumentNullException("nodes"); } if (key == null) { throw new ArgumentNullException("key"); } return(walker.GetChildren(nodes, key, null)); }
/// <summary> /// Gets all branches of a tree; a branch being a path from the root node to /// a leaf node. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="node"> /// The node that all branches will start from. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<System.Collections.Generic.IList<T>>"/> /// that contains all the branches. /// </returns> public static IEnumerable <IList <T> > GetBranches <T>( this ITreeWalker <T> walker, T node) { if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to keep track of the branches being traversed. Stack <IEnumerator <T> > enumerators = new Stack <IEnumerator <T> >(); enumerators.Push(Enumerable.Repeat(node, 1).GetEnumerator()); // Loop as long as there are enumerators on the stack. while (enumerators.Count > 0) { // Push enumerators onto the stack until we push an empty enumerator onto the // stack. This is how we construct a branch. while (enumerators.Peek().MoveNext()) { enumerators .Push( walker .GetChildren( enumerators .Peek() .Current) .GetEnumerator()); } // The top enumerator does not have any items; pop it off the stack and yield the // current item from each enumerator on the stack in reverse. This is a branch. enumerators.Pop().Dispose(); yield return(enumerators.ToReverseArray(x => x.Current)); // Pop enumerators off the stack until the stack is empty or we get to an // enumerator that has a next item. while (enumerators.Count > 0 && !enumerators.Peek().MoveNext()) { enumerators.Pop().Dispose(); } // If there is an enumerator on the stack then get the children of the enumerators // current item and push the enumerator of the children onto the stack. We are // ready to start building the next branch. if (enumerators.Count > 0) { enumerators .Push( walker .GetChildren( enumerators .Peek() .Current) .GetEnumerator()); } } }
/// <summary> /// Enumerates a tree using the level-order traversal method. I.e. it /// returns all nodes in the first level relative to the specified node, /// followed by all nodes in the second level, etc... /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the /// parent and child nodes. /// </param> /// <param name="node"> /// The root node of the tree that is to be traversed. /// </param> /// <param name="excludeSubtreePredicate"> /// A <see cref="System.Func<T, int, bool>"/> that determines if /// the current node that is being evaluated (and all of its descendants) /// should be included in the traversal. This allows for short-circuiting /// of the level-order traversal by excluding particular subtrees from /// the traversal. The first argument is the current node being evaluated /// and the second argument is the depth of the current node relative to /// the original node that the traversal began on. /// </param> /// <param name="excludeOption"> /// Used in conjunction with the <paramref /// name="excludeSubtreePredicate"/>. Determines if the entire subtree /// should be excluded or just its children. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that /// contains all the nodes in the tree ordered based on a level-order /// traversal. /// </returns> public static IEnumerable <T> LevelOrderTraversal <T>( this ITreeWalker <T> walker, T node, Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Set the initial depth to 0. int depth = 0; // Set 'node' as the current level. List <IEnumerable <T> > currentLevel = new List <IEnumerable <T> >() { new T[] { node } }; List <IEnumerable <T> > nextLevel = new List <IEnumerable <T> >(); // Track whether the current level has any nodes. This is true // initially since the current level is simply the node that was // passed to this function. bool currentLevelHasNodes = true; // Enumerate 'currentLevel' and yield each node while adding that // node's children to 'nextLevel'. When complete: set 'currentLevel' // equal to 'nextLevel', increment 'depth' and repeat the process. while (currentLevelHasNodes) { // Set current level has nodes to false when we enter the loop. // This will be set to true again if any nodes are concatenated // to 'nextLevel'. currentLevelHasNodes = false; foreach (T currentNode in currentLevel.SelectMany(x => x)) { // If the 'excludeSubtreePredicate' is not null and evaluates // to true then exlucude this node and all of its // descendants, depending on 'excludeOption', from the // traversal result. if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(currentNode, depth)) { if (excludeOption == ExcludeOption.ExcludeDescendants) { yield return(currentNode); } } else { yield return(currentNode); nextLevel.Add(walker.GetChildren(currentNode)); currentLevelHasNodes = true; } } // Swap the lists. I am reusing lists rather than simply creating // a new empty list so that there are fewer objects to be garbage // collected. var temp = currentLevel; currentLevel = nextLevel; nextLevel = temp; nextLevel.Clear(); // Increment the depth counter. depth++; } }
/// <summary> /// Enumerates a tree using the post-order traversal method. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child nodes. /// </param> /// <param name="node">The root node of the tree that is to be traversed.</param> /// <param name="excludeSubtreePredicate"> /// A <see cref="System.Func<T, Int32, Boolean>"/> that determines if the current node /// that is being evaluated (and all of its descendants) should be included in the /// traversal. This allows for short-circuiting of the post-order traversal by excluding /// particular subtrees from the traversal. The first argument is the current node being /// evaluated and the second argument is the depth of the current node relative to the /// original node that the traversal began on. /// </param> /// <param name="excludeOption"> /// Used in conjunction with the <paramref name="excludeSubtreePredicate"/>. Determines /// if the entire subtree should be excluded or just its children. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// nodes in the tree ordered based on a post-order traversal. /// </returns> public static IEnumerable <T> PostOrderTraversal <T>( this ITreeWalker <T> walker, T node, Func <T, int, bool> excludeSubtreePredicate, ExcludeOption excludeOption) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (node == null) { throw new ArgumentNullException("node"); } // Create a stack to keep track of the branches being traversed. Stack <IEnumerator <T> > enumerators = new Stack <IEnumerator <T> >(); enumerators.Push(Enumerable.Repeat(node, 1).GetEnumerator()); // Loop as long as we have an enumerator on the stack. When we have popped the last // one off the stack the traversal is complete. while (enumerators.Count > 0) { if (enumerators.Peek().MoveNext()) { node = enumerators.Peek().Current; // If the 'excludeSubtreePredicate' is not null and evaluates to true then // yield the current node if 'excludeOption' is set to exclude the children. // Otherwise, do not yield anything. if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(node, enumerators.Count - 1)) { if (excludeOption == ExcludeOption.ExcludeDescendants) { yield return(node); } } else { // Push the current node's children onto the stack. enumerators.Push(walker.GetChildren(node).GetEnumerator()); } } else { // Pop the enumerator and dispose of it. enumerators.Pop().Dispose(); // Yield the 'Current' value of the enumerator on the top of the stack. if (enumerators.Count > 0) { yield return(enumerators.Peek().Current); } } } //// If the 'excludeSubtreePredicate' is not null and evaluates to true then return //// 'node' or return an empty collection, depending on 'excludeOption'. //if (excludeSubtreePredicate != null && excludeSubtreePredicate.Invoke(node, 0)) //{ // if (excludeOption == ExcludeOption.ExcludeDescendants) // { // yield return node; // } // yield break; //} //else //{ // // Create stacks to keep track of the branches being traversed. // Stack<IEnumerator<T>> enumerators = new Stack<IEnumerator<T>>(); // Stack<T> nodes = new Stack<T>(); // // Push the current node and its children onto the stacks. // nodes.Push(node); // enumerators.Push(walker.GetChildren(node).GetEnumerator()); // // Loop as long as we have a node on the stack. When we have popped the last node // // off the stack the traversal is complete. // while (nodes.Count > 0) // { // // Try and move to the current node's next child. // if (enumerators.Peek().MoveNext()) // { // // If we successfully moved to the next child then set the current node // // to that child. // node = enumerators.Peek().Current; // // If the 'excludeSubtreePredicate' is not null and evaluates to true then // // return yield the current node if 'excludeOption' is set to exclude the // // children. Otherwise, do not yield anything. // if (excludeSubtreePredicate != null && // excludeSubtreePredicate.Invoke(node, nodes.Count)) // { // if (excludeOption == ExcludeOption.ExcludeDescendants) // { // yield return node; // } // } // else // { // // Push the current node and its children onto the stacks. // nodes.Push(node); // enumerators.Push(walker.GetChildren(node).GetEnumerator()); // } // } // else // { // // If the current node does not have any more children then pop it off of // // the 'nodes' stack and pop its children enumerator off the 'enumerators' // // stack. // // Yield the node that was popped off the stack. // enumerators.Pop().Dispose(); // yield return nodes.Pop(); // } // } //} }
/// <summary> /// Gets the nearest descendants based on a predicate. /// </summary> /// <typeparam name="T">The type of elements in the tree.</typeparam> /// <param name="walker"> /// The <see cref="ITreeWalker<T>"/> that knows how to find the parent and child /// nodes. /// </param> /// <param name="nodes"> /// The root nodes to be queried. /// </param> /// <param name="predicate"> /// A predicate to test each node for selection. The first argument is the current node /// being evaluated and the second argument is the depth of the current node relative to /// the original node that the query began on. /// </param> /// <returns> /// An <see cref="System.Collections.Generic.IEnumerable<T>"/> that contains all the /// matching nodes in the tree ordered based on a pre-order traversal. /// </returns> public static IEnumerable <T> GetDescendants <T>( this ITreeWalker <T> walker, IEnumerable <T> nodes, Func <T, int, bool> predicate) { // Validate parameters. if (walker == null) { throw new ArgumentNullException("walker"); } if (nodes == null) { throw new ArgumentNullException("nodes"); } if (predicate == null) { throw new ArgumentNullException("predicate"); } // Create a stack to keep track of the branches being traversed. Stack <IEnumerator <T> > enumerators = new Stack <IEnumerator <T> >(); foreach (T node in nodes) { enumerators .Push( walker .GetChildren(node) .GetEnumerator()); while (enumerators.Count > 0) { // Try and move to the current node's next child. if (enumerators.Peek().MoveNext()) { // If we successfully moved to the next child then set the current node // to that child. T currentNode = enumerators.Peek().Current; // If the predicate evaluates to true then yield the current node. // Otherwise, push the node and its enumerator to the stacks. if (predicate(currentNode, enumerators.Count)) { yield return(currentNode); } else { enumerators .Push( walker .GetChildren(currentNode) .GetEnumerator()); } } else { // If the current node does not have any more children then pop it off of // the 'nodes' stack and pop its children enumerator off the 'enumerators' // stack. enumerators.Pop().Dispose(); } } } }