LINQ:如何将嵌套的层次结构对象转换为扁平化对象

14

如何使用LINQ将嵌套的分层对象转换为扁平化的对象?我知道我们可以很容易地使用foreach循环来实现。但我想知道是否有一种方法可以使用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 实现这个功能?

2个回答

19

如果您希望对任意深度的人员树进行扁平化处理,我建议使用以下方法:

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没有真正好的方法来缩短这个问题,因为匿名lambda表达式不能自我递归调用而不实现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;

但对我来说似乎有些不必要。


当然,Lambda表达式可以调用自身。这里是使用递归Lambda表达式的斐波那契数列示例:Func<int, int> fib = null; fib = i => i <= 1 ? i : fib(i-1) + fib(i-2); - Allon Guralnek
1
我说“匿名lambda不能调用自身”,这就是为什么他无法编写返回所需值的单个表达式 - 他需要声明一个命名函数以进行递归。 - mqp
通过将匿名内容放入变量中,它就不再是匿名的了吗?例如 var a = new { X = 5 };?我仍然会称 a 引用的是一个匿名类型。微软无条件地表示 "Lambda 表达式是匿名函数",并查看 匿名方法 的第二个示例。几乎所有匿名内容都必须放在某种命名变量或参数中;否则它们不能被代码使用。这并不意味着它们不是匿名的。 - Allon Guralnek
1
我将继续使用“匿名”一词来表示“未经姓名确认”。在你的例子中,类型是匿名的,但对象不是匿名的。 - mqp
4
你只是在以一种我认为不常用的方式使用词语。我向维基百科页面求证:http://en.wikipedia.org/wiki/Anonymous_function “在编程语言理论中,匿名函数(也称函数常量、函数字面量或lambda函数)是一个未绑定到标识符的函数(或子程序),并可能被定义和调用。”说“对象没有名称”不符合我对“名称”的理解,因此我们在这方面存在分歧。 - mqp
显示剩余2条评论

8

这是一个很好的、通用的、可重复使用的扩展方法:

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

一个小问题是它的列表中不包括原父级元素,你需要自行添加。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接