Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LINQ: How to convert the nested hierarchical object to flatten object

Tags:

linq

How to convert the nested hierarchical object to flatten objects by using LINQ? I know that we can easily use foreach loop to achieve that. But I'm wondering if there is a way to write it in LINQ.

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

Data :

ID   : 1

Name : Jack

Children

2 | Rose 

3 | Paul

I like to convert this data into flatten format like below.

1 | Jack 

2 | Rose 

3 | Paul

How can we do it with Linq?

like image 217
Michael Sync Avatar asked Dec 21 '09 05:12

Michael Sync


2 Answers

If you want it to flatten an arbitrarily deep tree of people, I suggest the following:

public IEnumerable<Person> GetFamily(Person parent)
{
    yield return parent;
    foreach (Person child in parent.Children) // check null if you must
        foreach (Person relative in GetFamily(child))
            yield return relative;
}

There isn't really any good way to shorten this with LINQ, because anonymous lambdas can't call themselves recursively without implementing Y. You could "reduce" the above method to

return parent.Children.SelectMany(p => GetFamily(p))
                      .Concat(new Person[] { parent });

or alternatively

yield return parent;
    foreach (Person relative in parent.Children.SelectMany(GetFamily))
        yield return relative;

but that seems sort of unnecessary to me.

like image 196
mqp Avatar answered Oct 22 '22 14:10

mqp


This is a nice, generic and reusable extension method:

static public IEnumerable<T> Descendants<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> descendBy)
{
    if (!source.IsNullOrEmpty())
    {
        foreach (T value in source)
        {
            yield return value;

            if (!descendBy(value).IsNullOrEmpty())
            {
                foreach (T child in descendBy(value).Descendants<T>(descendBy))
                {
                    yield return child;
                }
            }
        }
    }
}

In the case above, use like this:

var allChildren = parent.Children.Descendants(p => p.Children);

One minor nit is that it doesn't include the original parent in the list, you'll need to do that.

like image 40
DarKow Avatar answered Oct 22 '22 14:10

DarKow