LINQ: как преобразовать вложенный иерархический объект в плоскость объекта

Как преобразовать вложенный иерархический объект для выравнивания объектов с помощью LINQ? Я знаю, что мы можем легко использовать foreach loop для достижения этого. Но мне интересно, есть ли способ записать его в LINQ.

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

Данные:

ID   : 1

Name : Jack

Children

2 | Rose 

3 | Paul

Мне нравится преобразовывать эти данные в формат сглаживания, как показано ниже.

1 | Jack 

2 | Rose 

3 | Paul

Как мы можем это сделать с Linq?

Ответы

Ответ 1

Если вы хотите, чтобы он сглаживал произвольно глубокое дерево людей, я предлагаю следующее:

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;
}

На самом деле нет хорошего способа сократить это с помощью LINQ, потому что анонимные лямбды не могут называть себя рекурсивно, не реализуя Y. Вы можете "уменьшить" вышеупомянутый метод до

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

или, альтернативно,

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

но это кажется мне ненужным.

Ответ 2

Это хороший, универсальный и многоразовый метод расширения:

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;
                }
            }
        }
    }
}

В приведенном выше примере используйте следующее:

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

Один незначительный nit состоит в том, что он не включает исходный родительский список в списке, вам нужно это сделать.