Skip to content

Instantly share code, notes, and snippets.

@mstum
Last active July 10, 2017 14:26
Show Gist options
  • Save mstum/d3bd19f2c06ea072070806c60b2e4847 to your computer and use it in GitHub Desktop.
Save mstum/d3bd19f2c06ea072070806c60b2e4847 to your computer and use it in GitHub Desktop.
public static class TreeDrawer
{
/// <summary>
/// Output a Tree to the console
/// </summary>
/// <remarks>
/// Example Output:
///
/// NodeName
/// ├─ NodeName
/// ├─ NodeName
/// │ ├─ NodeName
/// │ ├─ NodeName
/// │ │ ├─ NodeName
/// │ │ └─ NodeName
/// │ └─ NodeName
/// └ NodeName
/// </remarks>
/// <typeparam name="T"></typeparam>
/// <param name="rootNode">The root node of the tree</param>
/// <param name="getNodeName">A func that gets the Name of the node</param>
/// <param name="getNodeChildren">A func that gets the Children of the node. Must have an indexer.</param>
/// <param name="outputFunc">A func to which we'll send characters as we go. For Console output, use <see cref="Console.Write(string)"/>. Newlines will be sent as \r\n.</param>
public static void DrawTree<T>(T rootNode, Func<T, string> getNodeName, Func<T, IList<T>> getNodeChildren, Action<string> outputFunc)
{
DrawTreeNode(rootNode, getNodeName, getNodeChildren, outputFunc, 0, new HashSet<int>());
}
private static void DrawTreeNode<T>(T node, Func<T, string> getNodeName, Func<T, IList<T>> getNodeChildren, Action<string> outputFunc, int currentIndent, HashSet<int> columnsThatHaveEnded)
{
const string HBar = "│";
const string NodeWithFutureSiblings = "├─ ";
const string NodeWithoutFutureSiblings = "└─ ";
const string ColumnSpacing = " ";
for (int i = 0; i < currentIndent - 1; i++)
{
outputFunc(columnsThatHaveEnded.Contains(i) ? " " : HBar);
outputFunc(ColumnSpacing);
}
if (currentIndent > 0)
{
outputFunc(columnsThatHaveEnded.Contains(currentIndent - 1) ? NodeWithoutFutureSiblings : NodeWithFutureSiblings);
}
outputFunc(getNodeName(node));
outputFunc("\r\n");
var children = getNodeChildren(node);
var cc = children?.Count;
if (cc.HasValue && cc.Value > 0)
{
for (int ix = 0; ix < cc.Value; ix++)
{
var child = children[ix];
if (ix == cc.Value - 1)
{
columnsThatHaveEnded.Add(currentIndent);
}
DrawTreeNode(child, getNodeName, getNodeChildren, outputFunc, currentIndent + 1, columnsThatHaveEnded);
}
// Reset for the next sibling, since the HashSet is shared between siblings
columnsThatHaveEnded.Remove(currentIndent);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment