namespace HrynCo.Common.Tree; public static class TreeUtils { public static HashSet CollectDescendants( TKey rootId, IEnumerable nodes, TKey rootMarker) where TNode : ITreeNode where TKey : struct { var map = nodes .GroupBy(x => x.ParentId ?? rootMarker) .ToDictionary(g => g.Key, g => g.ToList()); var result = new HashSet { rootId }; var queue = new Queue(); queue.Enqueue(rootId); while (queue.Count > 0) { TKey current = queue.Dequeue(); if (!map.TryGetValue(current, out var children)) { continue; } foreach (TNode child in children.Where(child => result.Add(child.Id))) { queue.Enqueue(child.Id); } } return result; } public static List> BuildBreadcrumb( TKey currentId, IReadOnlyDictionary map) where TNode : class, ITreeNode, INameNode where TKey : struct { var path = new List>(); TNode? current = map.GetValueOrDefault(currentId); while (current != null) { path.Add(new BreadcrumbNode(current.Id, current.Name)); current = current.ParentId is { } parentId ? map.GetValueOrDefault(parentId) : null; } path.Reverse(); return path; } }