【问题标题】:Linq SelectMany based on two properties [duplicate]Linq SelectMany 基于两个属性[重复]
【发布时间】:2018-09-28 16:10:08
【问题描述】:

我有这门课:

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

现在我有一棵树,每个节点都有一个左右节点,每个节点都有左右节点等等。

我想获取树中所有节点的Names,但使用 SelectMany 无法做到。 我可以通过多种方式做到这一点,比如使用递归函数,但我真的很想知道它是如何使用 Linq 完成的。

tree.SelectMany(x=> new List<Node> {x.Left, x.Right}); 

上面的代码只返回2个节点(父节点的左右节点)。

【问题讨论】:

  • 没有内置的递归 LINQ 方法。所以最好的办法是你自己实现一个递归方法并调用它。
  • @TimSchmelter 我确实做到了,并得到了我想要的,但我想知道是否有一种我不知道的方法可以用 Linq 做到这一点,而不是调用另一种方法。
  • 您可以在 Stackoverflow 上找到 LINQ 方法。但它们非常复杂,而且通常效率低下。递归和LINQ不能很好地配合使用,所以我的建议是使用你的方法。
  • @TimSchmelter 非常感谢您的评论。正如我所说的那样,我已经这样做了,我在想有一种我想念的简单方法,我只是为了学习目的而不是为了项目而问这个问题。到目前为止,我已经选择了一个好的方法。

标签: c# linq


【解决方案1】:

LINQ 中的一切都基于接口IEnumerable。因此,要使您的 LINQ 正常工作,您必须将根 Node 树转换为 IEnumerable&lt;Node&gt;。我们可以使用扩展方法来做到这一点。

public static class NodeHelper
{
    public static IEnumerable<Node> ToEnumerable(this Node node)
    {
        var stack = new Stack<Node>();
        if (node != null) stack.Push(node);
        while (stack.Count > 0)
        {
            var current = stack.Pop();
            yield return current;
            if (current.Left != null) stack.Push(current.Left);
            if (current.Right != null) stack.Push(current.Right);
        }
    }
}

一旦你有了IEnumerable&lt;Node&gt;,你就可以做一个简单的Select()

foreach (var name in tree.ToEnumerable().Select(node => node.Name))
{
    Console.WriteLine(name);
}

您不需要使用SelectMany,因为您的Node 没有子节点IEnumerable&lt;Node&gt;,只有左右节点。当我们需要将多个序列合并为一个序列时,我们使用SelectMany

希望这会有所帮助。

【讨论】:

    【解决方案2】:

    这是我发现的:

    class Node
    {
        public Node() {Children = new List<Node>();}
    
        public IEnumerable<Node> GetSubTree()
        {
             return Children.SelectMany(c => c.GetSubTree()).Concat(new[] {this});
        }
    
        public List<Node> Children {get;set;}
    }
    
    class Tree
    {
        public Tree() {Roots = new List<Node>();}
    
        public IEnumerable<Node> GetAllNodes()
        {
             return Roots.SelectMany(root => root.GetSubTree());
        }
    
        List<Node> Roots {get;set;}
    }
    

    如果我是对的:当从 Tree 类调用 GetAllNodes() 时,它将获取树中每个根的所有节点。然后,如果您显然有一个名为 Name 的属性,则可以使用 this 获取名称:

    List<string> Names = YourList.Select(x => x.Name);
    

    希望对你有帮助!

    【讨论】:

    • 请注意我们没有树类,根是一个节点,有左右节点等等。所以树本身是typeof(Node)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-11-19
    • 2018-12-04
    • 1970-01-01
    相关资源
    最近更新 更多