关于c#:SelectMany以展平嵌套结构

SelectMany to flatten a nested structure

我正在分析XML结构,我的类如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class MyXml
{
    //...

    List<Node> Content { get; set; }

    //...
}

class Node
{
    // ...

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

    //...
}

myxml表示我正在解析的XML文件,其元素都称为。每个节点都有一个类型属性,可以有不同的值。

节点的类型未连接到其深度。我可以在任何深度级别使用任何节点类型。

我可以正确解析结构,所以我得到了一个myxml对象,它的内容是一个节点列表,列表中的任何节点都可以有子节点,等等(我使用了递归)。

我需要做的是展平整个结构,只提取特定类型的节点。

我尝试过:

1
var query = MyXml.Content.SelectMany(n => n.Nodes);

但它只取结构深度为1的节点。我想在同一个集合中抓取每个节点,不管深度如何,然后过滤我需要的内容。


这是一个自然递归的问题。使用递归lambda,尝试如下操作:

1
2
3
4
5
Func<Node, IEnumerable<Node>> flattener = null;
flattener = n => new[] { n }
    .Concat(n.Nodes == null
        ? Enumerable.Empty<Node>()
        : n.Nodes.SelectMany(flattener));

注意,当您这样做递归Func时,必须首先单独声明Func并将其设置为空。

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

1
2
3
4
5
6
7
8
9
10
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;
    }
}

不管怎样,一旦树被展平,就可以对展平的列表执行简单的LINQ查询以查找节点:

1
flattener(node).Where(n => n.Type == myType);

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


您应该实现一个方法Node.GetFlattened,它返回节点本身,然后在所有子节点上调用自己:

1
2
3
4
5
6
public IEnumerable<Node> GetFlattened()
{
    yield return this;
    foreach (var node in this.Nodes.SelectMany(n => n.GetFlattened()))
        yield return node;
}

然后可以调用这个方法,它递归地返回所有节点,而不管节点的深度如何。这是一个深度优先搜索,如果你想要一个广度优先搜索,你将不得不尝试另一种方法。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MyXml
{
    public List<Node> AllNodes()
    {
        List<Node> allNodes = new List<Node>();
        foreach (var node in Content)
            AddNode(node, nodes);
    }

    public void AddNode(Node node, List<Node> nodes)
    {
        nodes.Add(node);
        foreach (var childNode in node.Nodes)
            AddNode(childNode, nodes);
    }

    public List<Node> AllNodesOfType(NodeType nodeType)
    {
       return AllNodes().Where(n => n.NodeType == nodeType);
    }
}

首先用一个函数展平列表并对其进行查询。