Reputation: 5014
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?
Upvotes: 14
Views: 13673
Reputation: 89
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.
Upvotes: 8
Reputation: 72015
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.
Upvotes: 19