I have an object called Team that has a property Children which represents a list of "sub teams" under that team
public class Team
{
public List<Team> Children {get;set;}
}
I currently have some code that does this to build up a single list of all teams but as you can see its "hard coded" in terms of "levels" it goes down:
Team topTeam = GetTopteam();
List<Team> allTeams = new List<Team>();
allTeams.Add(topTeam);
allTeams.AddRange(topTeam.Children);
var childrensChildren = topTeam.Children.SelectMany(r=>r.Children);
allTeams.AddRange(childrensChildren);
and it keeps going . .
I now want to make the "levels" configurable so something like this:
public IEnumberable<Team> GetTeams(int numberOfLevelsDown)
{
}
If I pass in 1, i only return a list of the topteam and its direct children.
If I pass in 2, I get the top team, the children and the children's children
and so on . . .
What is the most elegant way of traversing down levels in a tree dynamically?
You can use recursion to do this.
This kind of method signature is because of performance issues and it prevents generating a lot of intermediate lists.
public void GetTeams(List<Team> teams, Team team, int level)
{
if (level == 0)
return;
if (team.Children == null)
return;
foreach (var t in team.Children)
{
teams.Add(t);
GetTeams(teams, t, level - 1);
}
}
and use it like this
var list = new List<Team>();
Team topTeam = GetTopteam();
GetTeams(list, topTeam, 5);
//now you have teams in list
I have used this kind of thing many times.
public static IEnumerable<T> EnumerateDescendants<T>(this T root, Func<T, IEnumerable<T>> children)
{
yield return root;
foreach (var child in children(root).SelectMany(x => x.EnumerateDescendants(children)))
{
yield return child;
}
}
Use it like this:
var allTeams = rootTeam.EnumerateDescendants(x => x.Children);
You should be able to modify it something like this:
public static IEnumerable<T> EnumerateDescendants<T>(this T root, Func<T, IEnumerable<T>> children, int maxLevels, int currentLevel = 0)
{
if (currentLevel <= maxLevels)
{
yield return root;
foreach (var child in children(root).SelectMany(x => x.EnumerateDescendants(children, maxLevels, currentLevel + 1)))
{
yield return child;
}
}
}
You should be able to use it like this:
var allTeamsUpToLevel2 = rootTeam.EnumerateDescendants(x => x.Children, 2);
I may have the >= mixed up but something like this should work.
Related
Class structure
public clas Item
{
public Item Parent { get; set; }
public string Code { get; set; }
}
example tree
AAA
- AAB
- BBB
CCC
- CCA
So i want to filter tree by CODE == BBB and result should be
AAA
- AAB
- BBB
but if i filter like this
IQueryable<Item> itemsQuery = GetQuery();
itemsQuery = itemsQuery.Where(x => x.Code == "BBB")
result does not contain parent nodes. So, how to include parent nodes, if their child nodes satisfy certain conditions?
In simple way, you cannot get recursive tree using EF. EF returns flat collections. However, there are workarounds.
Variant 1:
Add to your Item public Item Root { get; set; } and public ICollection<Item> AllSiblings { get; set; } property, which points all items to the actual root and second is the other way (all nested items).
The query than make look like:
IQueryable<Item> itemsQuery = GetQuery().Include(x => x.AllSiblings);
itemsQuery = itemsQuery.Where(x => x.Code == "BBB" || x.AllSiblings.Any(s => s.Code == "BBB")).ToList();
Now you have all items in your app and you can than recursively make the tree in C#.
Variant 2:
You can make several SQL queries to get each parent of found items. This is not recommended, because it will get very slow on more found results.
It is hard to apply LinQ here since there is no reference from parent items to children. What about doing simple enumeration to parent after applying filter? It will give you list of all matched items in the tree and then you'll might need to print it in a "tree" manner. Here is a BFS example
IQueryable<Item> itemsQuery = items.AsQueryable();
itemsQuery = itemsQuery.Where(x => x.Code == "BBB");
var bfsQueue = new Queue<Item>(itemsQuery);
var matchedItemsSet = new HashSet<Item>();
while (bfsQueue.Count > 0) {
var item = bfsQueue.Dequeue();
matchedItemsSet.Add(item);
var parent = item.Parent;
if (parent != null && !matchedItemsSet.Contains(parent))
{
bfsQueue.Enqueue(parent);
}
}
foreach (var item in matchedItemsSet) {
Console.WriteLine(item.Code);
}
I prefer universal approaches.
public static IEnumerable<T> SelectUntil<T>(this T element, Func<T, T> nextMemberSelector, Func<T, bool> stopCondition)
{
while (!stopCondition(element))
{
yield return element;
element = nextMemberSelector(element);
}
}
public static IEnumerable<Item> GetAncestors(this Item e)
{
// Or don't Skip(1) if you need the child itself included.
return e.SelectUntil(T => T.Parent, T => T.Parent == null).Skip(1);
}
private static void Main(string[] args)
{
IEnumerable<Item> itemsQuery = GetQuery();
IEnumerable<Item> filter = itemsQuery.Where(T => T.Code == "BBB");
foreach (Item item in filter)
{
Item[] allParents = item.GetAncestors().ToArray();
}
}
I find myself regularly writing recursive IEnumerable<T> iterators to implement the same "Descendants" pattern as provided by, for example, XContainer.Descendants. The pattern I keep implementing is as follows, given a type Foo with a single-level iterator called Children:
public static IEnumerable<Foo> Descendants(this Foo root) {
foreach (var child in root.Children()) {
yield return child;
foreach (var subchild in child.Descendants()) {
yield return subchild;
}
}
}
This old StackOverflow question suggests the same pattern. But for some reason it feels weird to me to have to reference three levels of heirarchy (root, child, and subchild). Can this fundamental depth-first recursion pattern be further reduced? Or is this an algorithmic primitive of sorts?
The best I can come up with is to abstract the pattern to a generic extension. This doesn't reduce the logic of the iterator pattern presented above, but it does remove the requirement of defining a Descendants method for multiple specific classes. On the downside, this adds an extension method to Object itself, which is a little smelly:
public static IEnumerable<T> SelectRecurse<T>(
this T root, Func<T, IEnumerable<T>> enumerator) {
foreach (T item in enumerator(root))
{
yield return item;
foreach (T subitem in item.SelectRecurse(enumerator))
{
yield return subitem;
}
}
}
// Now we can just write:
foreach(var item in foo.SelectRecurse(f => f.Children())) { /* do stuff */ }
You can use an explicit stack, rather than implicitly using the thread's call stack, to store the data that you are using. This can even be generalized to a Traverse method that just accepts a delegate to represent the "get my children" call:
public static IEnumerable<T> Traverse<T>(
this IEnumerable<T> source
, Func<T, IEnumerable<T>> childrenSelector)
{
var stack = new Stack<T>(source);
while (stack.Any())
{
var next = stack.Pop();
yield return next;
foreach (var child in childrenSelector(next))
stack.Push(child);
}
}
Because this isn't recursive, and thus isn't creating the state machines constantly, it will perform quite a bit better.
Side note, if you want a Breath First Search just use a Queue instead of a Stack. If you want a Best First Search use a priority queue.
To ensure that siblings are returned in the same order as they are returned from the selecor's order, rather than the reverse, just add a Reverse call to the result of childrenSelector.
I think this is a good question. The best explanation I have for why you need two loops: We need to recognize the fact that each item is converted to become multiple items (itself, and all its descendants). This means that we do not map one-to-one (like Select) but one-to-many (SelectMany).
We could write it like this:
public static IEnumerable<Foo> Descendants(this IEnumerable<Foo> items) {
foreach (var item in items) {
yield return item;
foreach (var subitem in item.Children().Descendants())
yield return subitem;
}
}
Or like this:
public static IEnumerable<Foo> Descendants(Foo root) {
var children = root.Children();
var subchildren = children.SelectMany(c => c.Descendants());
return children.Concat(subchildren);
}
Or like this:
public static IEnumerable<Foo> Descendants(this IEnumerable<Foo> items) {
var children = items.SelectMany(c => c.Descendants());
return items.Concat(children);
}
The versions taking an IEnumerable<Foo> must be invoked on root.Children().
I think all of these rewrites expose a different way of looking at the problem. On the other hand, they all have two nested loops. The loops can be hidden in helper functions but they still exist.
I would manage this with a List:
public static IEnumerable<Foo> Descendants(this Foo root) {
List<Foo> todo = new List<Foo>();
todo.AddRange(root.Children());
while(todo.Count > 0)
{
var first = todo[0];
todo.RemoveAt(0);
todo.InsertRange(0,first.Children());
yield return first;
}
}
Not recursive, so shouldn't blow the stack. You just always add more work for yourself onto the front of the list and so you achieve the depth-first traversal.
Both Damien_the_Unbeliever and Servy have presented versions of an algorithm that avoid creating a recursive call stack by using collections of one type or another. Damien's use of a List could cause poor performance to inserts at the head of the list, while Servy's use a of stack will cause nested elements to be returned in reverse order. I believe manually implementing a one-way linked list will maintain Servy's performance while still returning all the items in the original order. The only tricky part is initializing the first ForwardLinks by iterating the root. To keep Traverse clean I moved that to a constructor on ForwardLink.
public static IEnumerable<T> Traverse<T>(
this T root,
Func<T, IEnumerable<T>> childrenSelector) {
var head = new ForwardLink<T>(childrenSelector(root));
if (head.Value == null) yield break; // No items from root iterator
while (head != null)
{
var headValue = head.Value;
var localTail = head;
var second = head.Next;
// Insert new elements immediately behind head.
foreach (var child in childrenSelector(headValue))
localTail = localTail.Append(child);
// Splice on the old tail, if there was one
if (second != null) localTail.Next = second;
// Pop the head
yield return headValue;
head = head.Next;
}
}
public class ForwardLink<T> {
public T Value { get; private set; }
public ForwardLink<T> Next { get; set; }
public ForwardLink(T value) { Value = value; }
public ForwardLink(IEnumerable<T> values) {
bool firstElement = true;
ForwardLink<T> tail = null;
foreach (T item in values)
{
if (firstElement)
{
Value = item;
firstElement = false;
tail = this;
}
else
{
tail = tail.Append(item);
}
}
}
public ForwardLink<T> Append(T value) {
return Next = new ForwardLink<T>(value);
}
}
I propose a different version, without using yield:
public abstract class RecursiveEnumerator : IEnumerator {
public RecursiveEnumerator(ICollection collection) {
this.collection = collection;
this.enumerator = collection.GetEnumerator();
}
protected abstract ICollection GetChildCollection(object item);
public bool MoveNext() {
if (enumerator.Current != null) {
ICollection child_collection = GetChildCollection(enumerator.Current);
if (child_collection != null && child_collection.Count > 0) {
stack.Push(enumerator);
enumerator = child_collection.GetEnumerator();
}
}
while (!enumerator.MoveNext()) {
if (stack.Count == 0) return false;
enumerator = stack.Pop();
}
return true;
}
public virtual void Dispose() { }
public object Current { get { return enumerator.Current; } }
public void Reset() {
stack.Clear();
enumerator = collection.GetEnumerator();
}
private IEnumerator enumerator;
private Stack<IEnumerator> stack = new Stack<IEnumerator>();
private ICollection collection;
}
Usage example
public class RecursiveControlEnumerator : RecursiveEnumerator, IEnumerator {
public RecursiveControlEnumerator(Control.ControlCollection controlCollection)
: base(controlCollection) { }
protected override ICollection GetChildCollection(object c) {
return (c as Control).Controls;
}
}
To expand on my comment, this should work:
public static IEnumerable<Foo> Descendants(this Foo node)
{
yield return node; // return branch nodes
foreach (var child in node.Children())
foreach (var c2 in child.Descendants())
yield return c2; // return leaf nodes
}
That should will return all branch nodes and leaf nodes. If you only want to return leaf nodes, remove the first yield return.
In response to your question, yes it is an algorithmic primitive, because you definitely need to call node.Children(), and you definitely need to call child.Descendants() on each child. I agree that it seems odd having two "foreach" loops, but the second one is actually just continuing the overall enumeration, not iterating the children.
Try this:
private static IEnumerable<T> Descendants<T>(
this IEnumerable<T> children, Func<T, IEnumerable<T>> enumerator)
{
Func<T, IEnumerable<T>> getDescendants =
child => enumerator(child).Descendants(enumerator);
Func<T, IEnumerable<T>> getChildWithDescendants =
child => new[] { child }.Concat(getDescendants(child));
return children.SelectMany(getChildWithDescendants);
}
Or if you prefer the non Linq variant:
private static IEnumerable<T> Descendants<T>(
this IEnumerable<T> children, Func<T, IEnumerable<T>> enumerator)
{
foreach (var child in children)
{
yield return child;
var descendants = enumerator(child).Descendants(enumerator);
foreach (var descendant in descendants)
{
yield return descendant;
}
}
}
And call it like:
root.Children().Descendants(f => f.Children())
C# | .NET 4.5 | Entity Framework 5
I have a class in Entity Framework that looks like this:
public class Location
{
public long ID {get;set;}
public long ParentID {get;set;}
public List<Location> Children {get;set;}
}
ID is the identifier of the location, ParentID links it to a parent, and Children contains all of the children locations of the parent location. I'm looking for some easy way, likely recursively, to get all "Location" and their children to one single List containing the Location.ID's. I'm having trouble conceptualizing this recursively. Any help is appreciated.
This is what I have so far, its an extension to the entity class, but I believe it could be done better/simpler:
public List<Location> GetAllDescendants()
{
List<Location> returnList = new List<Location>();
List<Location> result = new List<Location>();
result.AddRange(GetAllDescendants(this, returnList));
return result;
}
public List<Location> GetAllDescendants(Location oID, ICollection<Location> list)
{
list.Add(oID);
foreach (Location o in oID.Children)
{
if (o.ID != oID.ID)
GetAllDescendants(o, list);
}
return list.ToList();
}
UPDATED
I ended up writing the recursion in SQL, throwing that in a SP, and then pulling that into Entity. Seemed cleaner and easier to me than using Linq, and judging by the comments Linq and Entity don't seem the best route to go. Thanks for all of the help!
You can do SelectMany
List<Location> result = myLocationList.SelectMany(x => x.Children).ToList();
You can use where condition for some selective results like
List<Location> result = myLocationList.Where(y => y.ParentID == someValue)
.SelectMany(x => x.Children).ToList();
If you only required Id's of Children you can do
List<long> idResult = myLocationList.SelectMany(x => x.Children)
.SelectMany(x => x.ID).ToList();
This will do the trick:
class Extensions
{
public static IEnumerable<T> SelectManyRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
var result = source.SelectMany(selector);
if (!result.Any())
{
return result;
}
return result.Concat(result.SelectManyRecursive(selector));
}
}
Use it like this:
List<Location> locations = new List<Location>();
//
// your code here to get locations
//
List<string> IDs = locations.SelectManyRecursive(l => l.Children).Select(l => l.ID).ToList();
Try this Extension method:
public static IEnumerable<T> Flatten<T, R>(this IEnumerable<T> source, Func<T, R> recursion) where R : IEnumerable<T>
{
return source.SelectMany(x => (recursion(x) != null && recursion(x).Any()) ? recursion(x).Flatten(recursion) : null)
.Where(x => x != null);
}
And you can use it like this:
locationList.Flatten(x => x.Children).Select(x => x.ID);
I had no Children prop in my model, so Nikhil Agrawal's answer doesn't work for me, so here is my solution.
With following model:
public class Foo
{
public int Id { get; set; }
public int? ParentId { get; set; }
// other props
}
You can get children of one item using:
List<Foo> GetChildren(List<Foo> foos, int id)
{
return foos
.Where(x => x.ParentId == id)
.Union(foos.Where(x => x.ParentId == id)
.SelectMany(y => GetChildren(foos, y.Id))
).ToList();
}
For ex.
List<Foo> foos = new List<Foo>();
foos.Add(new Foo { Id = 1 });
foos.Add(new Foo { Id = 2, ParentId = 1 });
foos.Add(new Foo { Id = 3, ParentId = 2 });
foos.Add(new Foo { Id = 4 });
GetChild(foos, 1).Dump(); // will give you 2 and 3 (ids)
I would like to contribute my own solution, which was modified from the references below:
public static IEnumerable<T> Flatten<T, R>(this IEnumerable<T> source, Func<T, R> recursion) where R : IEnumerable<T>
{
var flattened = source.ToList();
var children = source.Select(recursion);
if (children != null)
{
foreach (var child in children)
{
flattened.AddRange(child.Flatten(recursion));
}
}
return flattened;
}
Example:
var n = new List<FamilyMember>()
{
new FamilyMember { Name = "Dominic", Children = new List<FamilyMember>()
{
new FamilyMember { Name = "Brittany", Children = new List<FamilyMember>() }
}
}
}.Flatten(x => x.Children).Select(x => x.Name);
Output:
Dominic
Brittany
Class:
public class FamilyMember {
public string Name {get; set;}
public List<FamilyMember> Children { get; set;}
}
Ref. https://stackoverflow.com/a/21054096/1477388
Note: Can't find the other reference, but someone else on SO published an answer that I copied some code from.
Entity framework does not currently support recursion, and for that reason you can either
Rely on lazy loading child collections as you have done (beware the N+1 problem)
Query an arbitrary depth of objects (This will be an ugly query, though you could generate it using System.Linq.Expressions)
The only real option would be to avoid using LINQ to express the query, and instead resort to standard SQL.
Entity framework supports this scenario fairly well whether you're using code first or not.
For code-first, consider something along the lines of
var results = this.db.Database.SqlQuery<ResultType>(rawSqlQuery)
For model-first, consider using a defining query which I think is a good option as it allows further composition, or stored procedures.
To recursively get back data, you will need to understand recursive CTEs assuming you're using SQL Server, and that it is version 2005+
EDIT:
Here is the code for a recursive query to an arbitrary depth. I put this together just for fun, I doubt it would be very efficient!
var maxDepth = 5;
var query = context.Locations.Where(o => o.ID == 1);
var nextLevelQuery = query;
for (var i = 0; i < maxDepth; i++)
{
nextLevelQuery = nextLevelQuery.SelectMany(o => o.Children);
query = query.Concat(nextLevelQuery);
}
The flattened list is in the variable query
The accepted answer from #NikhilAgrawal will not recursively get all children and grandchildren as #electricalbah has pointed out.
I do miss the answer from #EricLippert that was given on Code Review.
https://codereview.stackexchange.com/a/5661/96658
static IEnumerable<T> DepthFirstTreeTraversal<T>(T root, Func<T, IEnumerable<T>> children)
{
var stack = new Stack<T>();
stack.Push(root);
while(stack.Count != 0)
{
var current = stack.Pop();
// If you don't care about maintaining child order then remove the Reverse.
foreach(var child in children(current).Reverse())
stack.Push(child);
yield return current;
}
}
Called like this:
static List<Location> AllChildren(Location start)
{
return DepthFirstTreeTraversal(start, c=>c.Children).ToList();
}
I made an example below with SelectMany. As you can see from Immediate Window you will not even get the Parent Id if you use that solution.
Create list to add all child using recursively
public static List list = new List();
recursive funtion
static void GetChild(int id) // Pass parent Id
{
using (var ctx = new CodingPracticeDataSourceEntities())
{
if (ctx.Trees.Any(x => x.ParentId == id))
{
var childList = ctx.Trees.Where(x => x.ParentId == id).ToList();
list.AddRange(childList);
foreach (var item in childList)
{
GetChild(item.Id);
}
}
}
}
Sample model
public partial class Tree
{
public int Id { get; set; }
public string Name { get; set; }
public Nullable<int> ParentId { get; set; }
}
Assuming Locations is a DbSet<Location> in your DB context, this will solve your problem "I'm looking for some easy way ... to get all 'Location' and their children to one single List containing the Location.ID's". Seems like I'm missing something, so please clarify if so.
dbContext.Locations.ToList()
// IDs only would be dbContext.Locations.Select( l => l.ID ).ToList()
This is my method for Flattening the children.
private Comment FlattenChildComments(Comment comment, ref Comment tempComment)
{
if (comment.ChildComments != null && comment.ChildComments.Any())
{
foreach (var childComment in comment.ChildComments)
{
tempComment.ChildComments.Add(childComment);
FlattenChildComments(childComment, ref tempComment);
}
}
comment.ChildComments = tempComment.ChildComments;
return comment;
}
For the people who needs something generic:
/// <summary>
/// Recursively enumerate all children, grandchildren etc... in a 1-dimentional IEnumerable
/// </summary>
/// <typeparam name="TModel">The type of the model</typeparam>
/// <param name="root">The root from which to enumerate children</param>
/// <param name="childSelector">The selector on how to select the children of the root.</param>
/// <returns>A 1-dimentional IEnumerable of all it's children, grandchildren etc.. recursively.</returns>
public static IEnumerable<TModel> EnumerateChildren<TModel>(TModel root, Func<TModel, IEnumerable<TModel>> childSelector)
{
var children = childSelector.Invoke(root);
if (children == null)
{
yield break;
}
foreach (var child in children)
{
yield return child;
foreach (var grandChild in EnumerateChildren(child, childSelector))
{
yield return grandChild;
}
}
}
Usage:
var location = GetLocation(); // Get your root.
var children = EnumerateChildren(location, l => l.Children);
Not sure how to call it, but say you have a class that looks like this:
class Person
{
public string Name;
public IEnumerable<Person> Friends;
}
You then have a person and you want to "unroll" this structure recursively so you end up with a single list of all people without duplicates.
How would you do this? I have already made something that seems to be working, but I am curious to see how others would do it and especially if there is something built-in to Linq you can use in a clever way to solve this little problem :)
Here is my solution:
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> subjects, Func<T, IEnumerable<T>> selector)
{
// Stop if subjects are null or empty
if(subjects == null)
yield break;
// For each subject
foreach(var subject in subjects)
{
// Yield it
yield return subject;
// Then yield all its decendants
foreach (var decendant in SelectRecursive(selector(subject), selector))
yield return decendant;
}
}
Would be used something like this:
var people = somePerson.SelectRecursive(x => x.Friends);
I don't believe there's anything built into LINQ to do this.
There's a problem with doing it recursively like this - you end up creating a large number of iterators. This can be quite inefficient if the tree is deep. Wes Dyer and Eric Lippert have both blogged about this.
You can remove this inefficiency by removing the direct recursion. For example:
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> subjects,
Func<T, IEnumerable<T>> selector)
{
if (subjects == null)
{
yield break;
}
Queue<T> stillToProcess = new Queue<T>(subjects);
while (stillToProcess.Count > 0)
{
T item = stillToProcess.Dequeue();
yield return item;
foreach (T child in selector(item))
{
stillToProcess.Enqueue(child);
}
}
}
This will also change the iteration order - it becomes breadth-first instead of depth-first; rewriting it to still be depth-first is tricky. I've also changed it to not use Any() - this revised version won't evaluate any sequence more than once, which can be handy in some scenarios. This does have one problem, mind you - it will take more memory, due to the queuing. We could probably alleviate this by storing a queue of iterators instead of items, but I'm not sure offhand... it would certainly be more complicated.
One point to note (also noted by ChrisW while I was looking up the blog posts :) - if you have any cycles in your friends list (i.e. if A has B, and B has A) then you'll recurse forever.
I found this question as I was looking for and thinking about a similar solution - in my case creating an efficient IEnumerable<Control> for ASP.NET UI controls. The recursive yield I had is fast but I knew that could have extra cost, since the deeper the control structure the longer it could take. Now I know this is O(n log n).
The solution given here provides some answer but, as discussed in the comments, it does change the order (which the OP did not care about). I realized that to preserve the order as given by the OP and as I needed, neither a simple Queue (as Jon used) nor Stack would work since all the parent objects would be yielded first and then any children after them (or vice-versa).
To resolve this and preserve the order I realized the solution would simply be to put the Enumerator itself on a Stack. To use the OPs original question it would look like this:
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> subjects, Func<T, IEnumerable<T>> selector)
{
if (subjects == null)
yield break;
var stack = new Stack<IEnumerator<T>>();
stack.Push(subjects.GetEnumerator());
while (stack.Count > 0)
{
var en = stack.Peek();
if (en.MoveNext())
{
var subject = en.Current;
yield return subject;
stack.Push(selector(subject).GetEnumerator());
}
else
{
stack.Pop().Dispose();
}
}
}
I use stack.Peek here to keep from having to push the same enumerator back on to the stack as this is likely to be the more frequent operation, expecting that enumerator to provide more than one item.
This creates the same number of enumerators as in the recursive version but will likely be fewer new objects than putting all the subjects in a queue or stack and continuing to add any descendant subjects. This is O(n) time as each enumerator stands on its own (in the recursive version an implicit call to one MoveNext executes MoveNext on the child enumerators to the current depth in the recursion stack).
You could use a non-recursive method like this as well:
HashSet<Person> GatherAll (Person p) {
Stack<Person> todo = new Stack<Person> ();
HashSet<Person> results = new HashSet<Person> ();
todo.Add (p); results.Add (p);
while (todo.Count > 0) {
Person p = todo.Pop ();
foreach (Person f in p.Friends)
if (results.Add (f)) todo.Add (f);
}
return results;
}
This should handle cycles properly as well. I am starting with a single person, but you could easily expand this to start with a list of persons.
Here's an implementation that:
Does a depth first recursive select,
Doesn't require double iteration of the child collections,
Doesn't use intermediate collections for the selected elements,
Doesn't handle cycles,
Can do it backwards.
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> rootItems, Func<T, IEnumerable<T>> selector)
{
return new RecursiveEnumerable<T>(rootItems, selector, false);
}
public static IEnumerable<T> SelectRecursiveReverse<T>(this IEnumerable<T> rootItems, Func<T, IEnumerable<T>> selector)
{
return new RecursiveEnumerable<T>(rootItems, selector, true);
}
class RecursiveEnumerable<T> : IEnumerable<T>
{
public RecursiveEnumerable(IEnumerable<T> rootItems, Func<T, IEnumerable<T>> selector, bool reverse)
{
_rootItems = rootItems;
_selector = selector;
_reverse = reverse;
}
IEnumerable<T> _rootItems;
Func<T, IEnumerable<T>> _selector;
bool _reverse;
public IEnumerator<T> GetEnumerator()
{
return new Enumerator(this);
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
class Enumerator : IEnumerator<T>
{
public Enumerator(RecursiveEnumerable<T> owner)
{
_owner = owner;
Reset();
}
RecursiveEnumerable<T> _owner;
T _current;
Stack<IEnumerator<T>> _stack = new Stack<IEnumerator<T>>();
public T Current
{
get
{
if (_stack == null || _stack.Count == 0)
throw new InvalidOperationException();
return _current;
}
}
public void Dispose()
{
_current = default(T);
if (_stack != null)
{
while (_stack.Count > 0)
{
_stack.Pop().Dispose();
}
_stack = null;
}
}
object System.Collections.IEnumerator.Current
{
get { return Current; }
}
public bool MoveNext()
{
if (_owner._reverse)
return MoveReverse();
else
return MoveForward();
}
public bool MoveForward()
{
// First time?
if (_stack == null)
{
// Setup stack
_stack = new Stack<IEnumerator<T>>();
// Start with the root items
_stack.Push(_owner._rootItems.GetEnumerator());
}
// Process enumerators on the stack
while (_stack.Count > 0)
{
// Get the current one
var se = _stack.Peek();
// Next please...
if (se.MoveNext())
{
// Store it
_current = se.Current;
// Get child items
var childItems = _owner._selector(_current);
if (childItems != null)
{
_stack.Push(childItems.GetEnumerator());
}
return true;
}
// Finished with the enumerator
se.Dispose();
_stack.Pop();
}
// Finished!
return false;
}
public bool MoveReverse()
{
// First time?
if (_stack == null)
{
// Setup stack
_stack = new Stack<IEnumerator<T>>();
// Start with the root items
_stack.Push(_owner._rootItems.Reverse().GetEnumerator());
}
// Process enumerators on the stack
while (_stack.Count > 0)
{
// Get the current one
var se = _stack.Peek();
// Next please...
if (se.MoveNext())
{
// Get child items
var childItems = _owner._selector(se.Current);
if (childItems != null)
{
_stack.Push(childItems.Reverse().GetEnumerator());
continue;
}
// Store it
_current = se.Current;
return true;
}
// Finished with the enumerator
se.Dispose();
_stack.Pop();
if (_stack.Count > 0)
{
_current = _stack.Peek().Current;
return true;
}
}
// Finished!
return false;
}
public void Reset()
{
Dispose();
}
}
}
use the Aggregate extension...
List<Person> persons = GetPersons();
List<Person> result = new List<Person>();
persons.Aggregate(result,SomeFunc);
private static List<Person> SomeFunc(List<Person> arg1,Person arg2)
{
arg1.Add(arg2)
arg1.AddRange(arg2.Persons);
return arg1;
}
Recursion is always fun. Perhaps you could simplify your code to:
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> subjects, Func<T, IEnumerable<T>> selector) {
// Stop if subjects are null or empty
if (subjects == null || !subjects.Any())
return Enumerable.Empty<T>();
// Gather a list of all (selected) child elements of all subjects
var subjectChildren = subjects.SelectMany(selector);
// Jump into the recursion for each of the child elements
var recursiveChildren = SelectRecursive(subjectChildren, selector);
// Combine the subjects with all of their (recursive child elements).
// The union will remove any direct parent-child duplicates.
// Endless loops due to circular references are however still possible.
return subjects.Union(recursiveChildren);
}
It will result in less duplicates than your original code. However their might still be duplicates causing an endless loop, the union will only prevent direct parent(s)-child(s) duplicates.
And the order of the items will be different from yours :)
Edit: Changed the last line of code to three statements and added a bit more documentation.
While its great to have IEnumerable when there might be a lot of data, its worth remembering the classic approach of recursively adding to a list.
That can be as simple as this (I've left out selector; just demonstrating recursively adding to an output list):
class Node
{
public readonly List<Node> Children = new List<Node>();
public List<Node> Flatten()
{
var all = new List<Node>();
Flatten(ref all);
return all;
}
public void Flatten(List<Node> all)
{
all.Add(this);
foreach (var child in Children)
child.Flatten(all);
}
}
usage:
Node rootNode = ...;
...
var all = rootNode.Flatten();
I've got a simple class defined as:
public class IndexEntry
{
public bool HighScore { get; set; }
public List<IndexEntry> SubEntries { get; set; }
//Other properties, etc...
}
I now need to search through a List to find the one item that has its HighScore Property set to true. Since it isn't a flat list, but a Hierarchy which can be an unknown number of levels deep and since the item I'm looking for might be contained in any one of the SubEnties lists, I can't do a simple Lambda like this:
var foundHighScore = myList.FirstOrDefault(IE => IE.HighScore == true);
Here's the code I have. I know it's ugly (at least it seems that way to me). It works, but is slow as sin on an even remotely large list and I'm sure there must be a better way.
private IndexEntry GetHighScoreEntry(IEnumerable<IndexEntry> entryList)
{
IndexEntry result = null;
IndexEntry recursiveResult = null;
foreach (IndexEntry currentEntry in entryList)
{
if (currentEntry.HighScore)
{
result = currentEntry;
break; //Don't need to look anymore, we found our highscore.;
}
else
{
if ((currentEntry.SubEntries == null) || (currentEntry.SubEntries.Count < 1))
{
continue;
}
else
{
recursiveResult = GetHighScoreEntry(currentEntry.SubEntries);
if (recursiveResult == null)
continue;
result = recursiveResult;
break;
}
}
}
return result;
}
I've got believe that there's a better way using a slightly more complex lambda or with LINQ to clean up this code and make it more performant.
Thanks in advance for your help.
All the solutions posted so far are specialized - they are not generic or general, and thus, the next time you have a hierarchical list, you'll have to code up a new solution. Yuck.
Here's a general, generic solution that will work for all your hierarchical needs:
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> sequence, Func<T, IEnumerable<T>> childFetcher)
{
var itemsToYield = new Queue<T>(sequence);
while (itemsToYield.Count > 0)
{
var item = itemsToYield.Dequeue();
yield return item;
var children = childFetcher(item);
if (children != null)
{
foreach (var child in children)
{
itemsToYield.Enqueue(child);
}
}
}
}
Here's how you'd use it:
myList.Flatten(i => i.SubEntries).FirstOrDefault(i => i.HighScore);
Easy as cheese.
This extension method can be used to turn any hierarchical data into a flat list, which can them be searched using LINQ.
Another great thing about this solution is that is uses lazy evaluation, thus it only does as much work as the caller demands. For example, in the above code, Flatten will stop churning out items as soon as a HighScore is found.
This solution also avoids recursion, which can be a costly operation for deeply nested hierarchies, avoiding the many stack allocations that recursive solutions incur.
Recursion is your friend here.
public IndexEntry FindHighScore(IEnumerable<IndexEntry> entries)
{
foreach (IndexEntry entry in entries)
{
IndexEntry highScore = FindHighScore(entry);
if (highScore != null)
{
return highScore;
}
}
return null;
}
private IndexEntry FindHighScore(IndexEntry entry)
{
return entry.HighScore ? entry : FindHighScore(entry.SubEntries);
}
You could significantly narrow your search down with a lambda expression something like:
var foundHighScore = myList.FirstOrDefault(IE => IE.HighScore or (IE.SubEntries != null && IE.SubEntries.Any(IES => IES.HighScore));
var indexEntry = foundHighScore;
if (!indexEntry.HighScore)
{
indexEntry = indexEntry.SubEntries.FirstOrDefault(IE => IE.HighScore);
}
// do something with indexEntry
Update
Actually the first solution won't traverse through properly. I don't think there is a lambda-only solution to this, you will have to do some form of recursive function. Off the top of my head the following would work, how it would cope performance wise I am unsure:
public IndexEntry FindHighScore(List<IndexEntry> entries)
{
var highScore = GetHighScore(entries);
if (highScore == null)
{
// give me only entries that contain sub-entries
var entriesWithSub = entries.Where(e => e.SubEntries != null);
foreach (var e in entriesWithSub)
{
highScore = FindHighScore(e.SubEntries);
if (highScore != null)
return highScore;
}
}
return highScore;
}
private IndexEntry GetHighScore(List<IndexEntry> entries)
{
return entries.FirstOrDefault(IE => IE.HighScore);
}