SelectMany以展平嵌套结构

vi3*_*i3x 2 c# linq list flatten

我正在解析XML结构,我的类看起来像如下:

class MyXml
{
    //...

    List<Node> Content { get; set; }

    //...
}

class Node
{
    // ...

    public List<Node> Nodes { get; set; }
    public string Type { get; set; }

    //...
}
Run Code Online (Sandbox Code Playgroud)

MyXml表示我正在解析的XML文件,其元素都被调用<node>.每个节点都有一个type属性,可以有不同的值.

节点的类型未与其深度相关联.我可以在任何深度级别拥有任何节点类型.

我可以正确地解析结构,所以我得到一个与myXML对象,其内容为节点,即列表中曾经节点可以有子节点等等(我用递归为)的列表.

我需要做的是展平整个结构并仅提取某种类型的节点.

我尝试过:

var query = MyXml.Content.SelectMany(n => n.Nodes);
Run Code Online (Sandbox Code Playgroud)

但它只采用结构深度为1的节点.我想在同一个集合中抓取每个节点,无论深度如何,然后过滤我需要的东西.

Yve*_* M. 8

这是一个自然递归的问题.使用递归lambda,尝试类似于:

Func<Node, IEnumerable<Node>> flattener = null;
flattener = n => new[] { n }
    .Concat(n.Nodes == null 
        ? Enumerable.Empty<Node>()
        : n.Nodes.SelectMany(flattener));
Run Code Online (Sandbox Code Playgroud)

请注意,当您Func像这样进行递归时,必须先Func单独声明,然后将其设置为null.

您还可以使用迭代器块方法展平列表:

public static IEnumerable<Node> Flatten(Node node)
{
    yield return node;
    if (node.Nodes != null)
    {
        foreach(var child in node.Nodes)
            foreach(var descendant in Flatten(child))
                yield return descendant;
    }
}
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,一旦树被展平,您可以在展平列表上执行简单的Linq查询以查找节点:

flattener(node).Where(n => n.Type == myType);
Run Code Online (Sandbox Code Playgroud)

响应改编自:https://stackoverflow.com/a/17086572/1480391