I wrote a few simple extension methods that allow to traverse a hierachically data structure depth-first or breath-first using LINQ:
Usage:
public class Item { public Item(string name, params Item[] children) { Name = name; Items = (children ?? new Item[0]).ToList(); } public string Name { get; private set; } public List<Item> Items { get; private set; } } void Main() { var hierachicalItems = new[] { new Item("A1", new Item("B1", new Item("C1"), new Item("C2")), new Item("B2")) }; var query = hierachicalItems.DepthFirst(i => i.Items).Select(i => i.Name); Console.WriteLine("DepthFirst:\t\t" + string.Join(", ", query)); query = hierachicalItems.BreadthFirst(i => i.Items).Select(i => i.Name); Console.WriteLine("BreadthFirst:\t" + string.Join(", ", query)); } Output:
DepthFirst: A1, B1, C1, C2, B2
BreadthFirst: A1, B1, B2, C1, C2
The TreeEnumerable class:
public static class TreeEnumerable { // BreathFirst // ========================================================== public static IEnumerable<TNode> BreadthFirst<TNode>(this IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> getSubNodes) { Queue<TNode> queue = new Queue<TNode>(256); foreach (TNode node in nodes) { queue.Enqueue(node); } return BreadthFirst(queue, getSubNodes); } private static IEnumerable<TNode> BreadthFirst<TNode>(Queue<TNode> queue, Func<TNode, IEnumerable<TNode>> getSubNodes) { TNode node; while (queue.Count > 0) { node = queue.Dequeue(); if (node == null) { yield break; } foreach (TNode subNode in getSubNodes(node)) { queue.Enqueue(subNode); } yield return node; } } public static IEnumerable<TNode> BreadthFirst<TNode>(this IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> getSubNodes, Boolean repeatNodes) { HashSet<TNode> passedNodes = new HashSet<TNode>(); Queue<TNode> queue = new Queue<TNode>(256); if (repeatNodes) { return nodes.BreadthFirst(getSubNodes); } foreach (TNode node in nodes) { if (passedNodes.Contains(node)) { continue; } passedNodes.Add(node); queue.Enqueue(node); } return BreadthFirst(queue, getSubNodes, passedNodes); } private static IEnumerable<TNode> BreadthFirst<TNode>(Queue<TNode> queue, Func<TNode, IEnumerable<TNode>> getSubNodes, HashSet<TNode> passedNodes) { TNode node; while (queue.Count > 0) { node = queue.Dequeue(); if (node == null) { yield break; } foreach (TNode subNode in getSubNodes(node)) { if (passedNodes.Contains(subNode)) { continue; } passedNodes.Add(subNode); queue.Enqueue(subNode); } yield return node; } } // DepthFirst // ========================================================== public static IEnumerable<TNode> DepthFirst<TNode>(this IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> getSubNodes) { foreach (TNode node in nodes) { yield return node; foreach (TNode subNode in getSubNodes(node).DepthFirst(getSubNodes)) { yield return subNode; } } } public static IEnumerable<TNode> DepthFirst<TNode>(this IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> getSubNodes, Boolean repeatNodes) { if (repeatNodes) { return nodes.DepthFirst(getSubNodes); } return nodes.DepthFirst(getSubNodes, new HashSet<TNode>()); } private static IEnumerable<TNode> DepthFirst<TNode>(this IEnumerable<TNode> nodes, Func<TNode, IEnumerable<TNode>> getSubNodes, HashSet<TNode> passedNodes) { foreach (TNode node in nodes) { if (passedNodes.Contains(node)) { continue; } passedNodes.Add(node); yield return node; foreach (TNode subNode in getSubNodes(node).DepthFirst(getSubNodes, passedNodes)) { yield return subNode; } } } } Any improvments or usefull enhancement?
KeyValue<string, object>pairs where each value may be another list ofKeyValue<string, object>pairs or something like that... \$\endgroup\$