Linq递归搜索节点

时间:2019-05-06 08:16:46

标签: c# linq

尝试获取所有不为空的List<Node>()的列表。子节点该怎么做?

public class Node
{
    public string Name { get; set; }

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

public class Program
{
    static void Main(string[] args)
    {
        List<Node> Items = new List<Node>();
        Items.Add(new Node { Name = "Test0" });
        Items.Add(new Node { Name = "Test1" });
        Items.Add(new Node { Name = "Test2" });
        Items.Add(new Node { Name = "Test3" });
        Items.Add(new Node { Name = "Test4" });
        Items.Add(new Node { Name = "Test5" });
        Items.Add(new Node
        {
            Name = "Test6",
            Nodes = new List<Node>
            {
                new Node
                {
                    Name = "Test6.1",
                    Nodes = new List<Node>
                    {
                        new Node
                        {
                            Name = "Test6.1.1", Nodes = new List<Node>()
                        }
                    }
                },

            }
        });
        Items.Add(new Node { Name = "Test7", Nodes = new List<Node> { } });
        Items.Add(new Node { Name = "Test8", Nodes = new List<Node> { } });

        var NotNullNodes = Items.SelectMany(m => m.Nodes);
    }
}

4 个答案:

答案 0 :(得分:3)

另一个linq递归解决方案:

public static IEnumerable<Node> GetAllNodes( Node root )
{
    if( root == null )
    {
        yield break;
    }

    yield return root;

    if ( root.Nodes == null )
    {
        yield break;
    }

    foreach ( Node descendant in root.Nodes.SelectMany( GetAllNodes ) )
    {
        yield return descendant;
    }
}

像这样使用:

Items.SelectMany( GetAllNodes )

答案 1 :(得分:1)

好吧,SelectMany仅使一级变平。在您的情况下,您需要对图形进行某种搜索,例如 BFS -宽度优先搜索

  public static partial class EnumerableExtensions {
    public static IEnumerable<T> BreadthFirstSearch<T>(
      this IEnumerable<T> source, 
      Func<T, IEnumerable<T>> children) {

      if (Object.ReferenceEquals(null, source))
        throw new ArgumentNullException(nameof(source));
      else if (Object.ReferenceEquals(null, children))
        throw new ArgumentNullException(nameof(children));

      HashSet<T> proceeded = new HashSet<T>();

      Queue<IEnumerable<T>> queue = new Queue<IEnumerable<T>>();

      queue.Enqueue(source);

      while (queue.Count > 0) {
        IEnumerable<T> src = queue.Dequeue();

        if (Object.ReferenceEquals(null, src))
          continue;

        foreach (var item in src) 
          if (proceeded.Add(item)) {
            yield return item;

            queue.Enqueue(children(item));
          }
      }
    }
  }

然后您可以放

var NotNullNodes = Items.BreadthFirstSearch(item => item.Items ?? new List<Node>()); 

答案 2 :(得分:0)

这是一个递归获取所有节点的函数:

    public static List<Node> GetAllNodes(List<Node> items)
    {
        List<Node> allNodes = new List<Node>();

        foreach(Node n in items)
            if (n.Nodes != null && n.Nodes.Count > 0)
                allNodes.AddRange(GetAllNodes(n.Nodes));

        allNodes.AddRange(items);

        return allNodes;
    }

答案 3 :(得分:0)

如果为IEnumerable创建扩展方法,则可以像使用现有LINQ方法一样使用它。参见extension methods demystified

static IEnumerable<Node> Flatten(IEnumerable<node> nodesWithSubNodes)
{
    // Todo: check nodesWithSubNodes not null
    foreach (var node in nodesWithSubNodes)
    {
        yield return node;
        if (node.SubNodes != null)  // not needed if you are certain that not null
        {
            foreach (var subNode in nodes.SubNodes.Flatten())
                yield return subNode;
        }
}

用法:

var result = myNodes.Flatten()
             .Take(3)
             .ToList();

对此的好处是,它看起来像现有的LINQ函数。它也是非常有效的,因为它不枚举比实际查询更多的元素,如示例中所示。