Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ to Get All heirerichal children

Tags:

c#

linq

I have been digging this quite a while.

public class Person
{
   public string Name { get; set; }    
   public string Age { get; set; }    
   public List<Person> Children { get; set; }    
}

I want a single LINQ query to find out "All the persons whose Age > 4 in this collection".

Note: You have to traverse Collection of Person + Collection of Children, so each children object will have a collection of Person till Children becomes null.

like image 809
anand mishra Avatar asked Mar 13 '23 10:03

anand mishra


2 Answers

Create a visitor. In this example by implementing a helper class:

public static class Helpers
public static IEnumerable<Person> GetDescendants(this Person person)
{
    foreach (var child in person.Children)
    {
        yield return child;
        foreach (var descendant in child.GetDescendants())
        {
           yield return descendant;
        }
    }
}

this is one of the times where the "yield return many" would be useful.

like image 33
Tormod Avatar answered Mar 19 '23 11:03

Tormod


First i can't understand why all your properties private and Age is not int type. So my class looks like this:

public partial class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public List<Person> Childrens { get; set; }
}

Note partial word. This word will allow you to place your class logic in separate files and this could be usefull when you creating some custom logic in class.

Then I simply create this method in different file:

public partial class Person
{
    public Person GetPersonWithChindren(int maxAge)
    {
        return new Person
        {
            Age = this.Age,
            Name = this.Name,
            Childrens = this.Childrens != null 
            ? this.Childrens
                .Where(x => x.Age < maxAge)
                .Select(x => x.GetPersonWithChindren(maxAge)) //this line do recursive magic
                .ToList() 
            : null
        };
    }
}

As you can see this method checking Age of each child and if Age is ok then it checks next level of hierarchy untill Childrens is null.

So you can use it like this:

var person = new Person()
{
  //initialisation of your collection here
}

//result will contains only nodes where Person have age < 4 and Childs that have age < 4
var result = person.GetPersonWithChindren(4);

Note that this solution will work normal with linqToEntities. But if you using LinqToSQL this expression produces query to DB on each Person entity. So if you have many Persons and deep hierarhy it will costs you a lot of machine time. In that case you should to write stored procedure with CTE instead of LinQ query.

UPDATE:

You even can write more general solution with a help of Func<T> class like this:

public partial class Person
{
    public Person GetPersonWithChindren(Func<Person, bool> func)
    {
        return new Person
        {
            Age = this.Age,
            Name = this.Name,
            Childrens = this.Childrens != null
            ? this.Childrens
                .Where(x => func(x))
                .Select(x => x.GetPersonWithChindren(func))
                .ToList()
            : null
        };
    }
}

And then you can use it like this:

var result = person.GetPersonWithChindren(x => x.Age < 4);

You always can change your criteria now where you want to use your function.

like image 158
teo van kot Avatar answered Mar 19 '23 13:03

teo van kot