Skip to content

Instantly share code, notes, and snippets.

@createdbyx
Last active August 29, 2015 14:14
Show Gist options
  • Save createdbyx/a58a826777421d90a86b to your computer and use it in GitHub Desktop.
Save createdbyx/a58a826777421d90a86b to your computer and use it in GitHub Desktop.
Constructs a nested hierarchy of types from a flat list of source types. (without recursive calling)
/*
Sample use case ...
class Program
{
static void Main(string[] args)
{
var paths = new[]
{
"assets/folder",
"assets/folder/filea.txt",
"assets/folder/fileb.txt",
"assets/folderb",
"assets/folderb/filex.txt",
"assets/folderb/filey.txt",
};
var separator = new[] { Path.AltDirectorySeparatorChar.ToString(CultureInfo.InvariantCulture) };
var nodes = paths.ToHierarchy(
x => x.Split(separator, StringSplitOptions.RemoveEmptyEntries),
(r, p) => string.CompareOrdinal(r.Name, p) == 0,
r => r.Nodes,
(r, c) => ((List<TreeViewNode>)r).Add(c),
CreateTreeNode);
foreach (var node in nodes)
{
WriteNode(node);
}
//outputs
//----------
//assets - assets
// folder - assets\folder
// filea.txt - assets\folder\filea.txt
// fileb.txt - assets\folder\fileb.txt
// folderb - assets\folderb
// filex.txt - assets\folderb\filex.txt
// filey.txt - assets\folderb\filey.txt
Console.ReadKey();
}
private static TreeViewNode CreateTreeNode(string[] parts, int index, string source)
{
var node = new TreeViewNode() { Name = parts[index] };
node.Value = string.Join(Path.DirectorySeparatorChar.ToString(CultureInfo.InvariantCulture), parts, 0, index + 1);
if (index == parts.Length - 1)
{
node.Name = Path.GetFileName(source);
}
return node;
}
public static void WriteNode(TreeViewNode node, int depth = 0)
{
Console.WriteLine("{0}{1} - {2}", new String(' ', depth), node.Name, node.Value);
foreach (var child in node.Nodes)
{
WriteNode(child, depth + 2);
}
}
}
public class TreeViewNode
{
public List<TreeViewNode> Nodes { get; set; }
public TreeViewNode()
{
this.Nodes = new List<TreeViewNode>();
this.IsExpanded = true;
}
public string Value { get; set; }
public string Name { get; set; }
public bool IsExpanded { get; set; }
public bool IsChecked { get; set; }
}
*/
public static class ExtensionMethods
{
/// <summary>
/// Constructs a nested hierarchy of types from a flat list of source types.
/// </summary>
/// <typeparam name="TSource">The source type of the flat list that is to be converted.</typeparam>
/// <typeparam name="TReturn">The type that will be returned.</typeparam>
/// <typeparam name="TPart">The type of the art type.</typeparam>
/// <param name="sourceItems">The source items to be converted.</param>
/// <param name="getParts">A callback function that returns a array of <see cref="TPart"/>.</param>
/// <param name="comparePart">The compare part callback.</param>
/// <param name="getChildren">The get children callback.</param>
/// <param name="addChild">The add child callback.</param>
/// <param name="createItem">The create item callback.</param>
/// <returns>Returns an collection of <see cref="TReturn"/> representing the hierarchy.</returns>
/// <exception cref="Exception">A delegate callback throws an exception. </exception>
public static IEnumerable<TReturn> ToHierarchy<TSource, TReturn, TPart>(
this IEnumerable<TSource> sourceItems,
Func<TSource, TPart[]> getParts,
Func<TReturn, TPart, bool> comparePart,
Func<TReturn, IEnumerable<TReturn>> getChildren,
Action<IEnumerable<TReturn>, TReturn> addChild,
Func<TPart[], int, TSource, TReturn> createItem)
{
var treeModels = new List<TReturn>();
foreach (var keyName in sourceItems)
{
IEnumerable<TReturn> items = treeModels;
var parts = getParts(keyName);
for (var partIndex = 0; partIndex < parts.Length; partIndex++)
{
var node = items.FirstOrDefault(x => comparePart(x, parts[partIndex]));
if (node != null)
{
items = getChildren(node);
continue;
}
var model = createItem(parts, partIndex, keyName);
addChild(items, model);
items = getChildren(model);
}
}
return treeModels;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment