Recursive Linq Query: Select Item and All Children with Subchildren

Recursive LINQ query: select item and all children with subchildren

   public class Comment
{
public int Id { get; set; }
public int ParentId { get; set; }
public string Text { get; set; }
public List<Comment> Children { get; set; }
}

class Program
{
static void Main()
{
List<Comment> categories = new List<Comment>()
{
new Comment () { Id = 1, Text = "Item 1", ParentId = 0},
new Comment() { Id = 2, Text = "Item 2", ParentId = 0 },
new Comment() { Id = 3, Text = "Item 3", ParentId = 0 },
new Comment() { Id = 4, Text = "Item 1.1", ParentId = 1 },
new Comment() { Id = 5, Text = "Item 3.1", ParentId = 3 },
new Comment() { Id = 6, Text = "Item 1.1.1", ParentId = 4 },
new Comment() { Id = 7, Text = "Item 2.1", ParentId = 2 }
};

List<Comment> hierarchy = new List<Comment>();
hierarchy = categories
.Where(c => c.ParentId == 0)
.Select(c => new Comment() {
Id = c.Id,
Text = c.Text,
ParentId = c.ParentId,
Children = GetChildren(categories, c.Id) })
.ToList();

HieararchyWalk(hierarchy);

Console.ReadLine();
}

public static List<Comment> GetChildren(List<Comment> comments, int parentId)
{
return comments
.Where(c => c.ParentId == parentId)
.Select(c => new Comment {
Id = c.Id,
Text = c.Text,
ParentId = c.ParentId,
Children = GetChildren(comments, c.Id) })
.ToList();
}

public static void HieararchyWalk(List<Comment> hierarchy)
{
if (hierarchy != null)
{
foreach (var item in hierarchy)
{
Console.WriteLine(string.Format("{0} {1}", item.Id, item.Text));
HieararchyWalk(item.Children);
}
}
}

Linq recursive parent child

For linq-to-objects you can define your own extension method on IEnumerable<T> that recursively gets all children.

public static class EnumerableExtensions
{
public static IEnumerable<T> SelectRecursive<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
{
foreach (var parent in source)
{
yield return parent;

var children = selector(parent);
foreach (var child in SelectRecursive(children, selector))
yield return child;
}
}
}

Usage:

var lookup = col.ToLookup(x => x.Parent_Id);
var res = lookup[null].SelectRecursive(x => lookup[x.Id]).ToList();

Get All Children to One List - Recursive C#

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();

Recursive linq to get infinite children

can you try this..

 public IEnumerable<Location> GetChild(int id)
{
DBEntities db = new DBEntities();
var locations = db.Locations.Where(x => x.ParentLocationID == id || x.LocationID == id).ToList();

var child = locations.AsEnumerable().Union(
db.Locations.AsEnumerable().Where(x => x.ParentLocationID == id).SelectMany(y => GetChild(y.LocationId))).ToList();
return child;
}

How to validate if a parent item has children in recursive LINQ function?

So testing the children earlier really isn't going to help you. You're still doing the same work, conceptually. If each recursive call handles two depths at once, instead of one, you're greatly complicating the method itself, duplicating the work of the second depth whenever there are children, and gaining very, very little even when there are no children. The expensive part of the method is in the linear search through the list for the children, not in the method call that starts the search.

To improve performance you need to stop doing linear searches through a very large list many, many times. Fortunately, this is easy enough to do. Just create a lookup once, at the start of the method, of all children for each parent.

var lookup = list.ToLookup(x => x.Field<int?>("LeaderID"));

Now, what you're trying to do, conceptually, is traverse a tree. We can start out with a generalized "traverse" method capable of traversing any tree (using a non-recursive implementation, for performance improvements):

public static IEnumerable<T> Traverse<T>(IEnumerable<T> source, 
Func<T, IEnumerable<T>> childSelector)
{
var queue = new Queue<T>(source);
while (queue.Any())
{
var item = queue.Dequeue();
yield return item;
foreach (var child in childSelector(item))
{
queue.Enqueue(child);
}
}
}

Now that we have these we can use the lookup to efficiently get all children for each node, greatly improving the implementation. Of course, it'd be prettier if you didn't also need the depth of each node, but it's still not that bad.

var roots = lookup[leader]
.Select(row => Tuple.Create(row.Field<int>("RepID"), 0));

return Traverse(roots, node => lookup[node.Item1]
.Select(row => Tuple.Create(row.Field<int>("RepID"), node.Item2 + 1)));

If you don't need to know the depth of each node you can simplify all of that down to this:

var lookup = list.ToLookup(
row => row.Field<int?>("LeaderID"),
row => row.Field<int>("RepID"));
return Traverse(lookup[leader], node => lookup[node]);

Querying nested collection (parent / children)

This works for me:

public static class Ex
{
public static List<Menu> CloneWhere(this List<Menu> source, Func<Menu, bool> predicate)
{
return
source
.Where(predicate)
.Select(x => new Menu()
{
Name = x.Name,
Link = x.Link,
Children = x.Children.CloneWhere(predicate)
})
.Where(predicate)
.ToList();
}
}

The sample data looks like this:

source

...then I can apply this:

var result = root.CloneWhere(m => m.Children.Any() || m.Link != null);

...and I get this result:

result



Related Topics



Leave a reply



Submit