This is similar to the question (Finding parents in a tree hierarchy for a given child LINQ (lambda expression)). However, instead of finding all ancestors, I need to find a
I am modifying Yacoub's method but only managed to get all descendants in one branch
This is because of this line:
var child = allRoles.FirstOrDefault(x => x.ParentId == role.Id);
While it might have been appropriate for finding a single parent, it's not appropriate for finding multiple children.
But you don't need recursive iterators and multiple iterations over the allRoles
list. You can create a fast lookup structure using ToLookup extension method and then perform iterative DFS like this:
private static IEnumerable<UserRole> FindAllChildren(List<UserRole> allRoles, UserRole role)
{
var childrenByParentId = allRoles.ToLookup(r => r.ParentId);
var stack = new Stack<IEnumerator<UserRole>>();
var e = childrenByParentId[role != null ? role.Id : (int?)null].GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
yield return e.Current;
stack.Push(e);
e = childrenByParentId[e.Current.Id].GetEnumerator();
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
}
}
finally
{
e.Dispose();
while (stack.Count > 0) stack.Pop().Dispose();
}
}
An even better approach would be (following the DRY principle) to utilize the generic tree helper method from How to flatten tree via LINQ?:
public static class TreeUtils
{
public static IEnumerable<T> Expand<T>(
this IEnumerable<T> source, Func<T, IEnumerable<T>> elementSelector)
{
var stack = new Stack<IEnumerator<T>>();
var e = source.GetEnumerator();
try
{
while (true)
{
while (e.MoveNext())
{
var item = e.Current;
yield return item;
var elements = elementSelector(item);
if (elements == null) continue;
stack.Push(e);
e = elements.GetEnumerator();
}
if (stack.Count == 0) break;
e.Dispose();
e = stack.Pop();
}
}
finally
{
e.Dispose();
while (stack.Count != 0) stack.Pop().Dispose();
}
}
}
like this:
private static IEnumerable<UserRole> FindAllChildren(List<UserRole> allRoles, UserRole role)
{
var childrenByParentId = allRoles.ToLookup(r => r.ParentId);
return childrenByParentId[role != null ? role.Id : (int?)null].Expand(r => childrenByParentId[r.Id]);
}