Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/311.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用lambda表达式从嵌套集合获取叶节点_C#_.net_Linq - Fatal编程技术网

C# 如何使用lambda表达式从嵌套集合获取叶节点

C# 如何使用lambda表达式从嵌套集合获取叶节点,c#,.net,linq,C#,.net,Linq,如何从下面的示例中获取结果集 public class Parent { public string Id { get; set; } public List<Child> Children { get; set; } } public class Child : Parent { public bool Isleaf { get; set; } } Child c1 = new Child(); c1.Id = "c1"; c1.Isleaf = fals

如何从下面的示例中获取结果集

public class Parent
{
    public string Id { get; set; }
    public List<Child> Children { get; set; }
}

public class Child : Parent
{
    public bool Isleaf { get; set; }
}
Child c1 = new Child();
c1.Id = "c1";
c1.Isleaf = false;

Child c2 = new Child();
c2.Id = "c2";
c2.Isleaf = true;

Child c11 = new Child();
c11.Id = "c11";
c11.Isleaf = true;

Child c12 = new Child();
c12.Id = "c12";
c12.Isleaf = false;


Child c121 = new Child();
c121.Id = "c121";
c121.Isleaf = true;

c12.Children = new List<Child>() { c121 };
c1.Children = new List<Child>() { c11, c12 };

Parent p = new Parent();
p.Id = "P1";
p.Children = new List<Child>() { c1, c2 };
公共类父类
{
公共字符串Id{get;set;}
公共列表子项{get;set;}
}
公共类子级:父级
{
公共布尔Isleaf{get;set;}
}
子项c1=新子项();
c1.Id=“c1”;
c1.Isleaf=false;
Child c2=新的Child();
c2.Id=“c2”;
c2.Isleaf=true;
Child c11=新的Child();
c11.Id=“c11”;
c11.Isleaf=true;
Child c12=新的Child();
c12.Id=“c12”;
c12.Isleaf=false;
Child c121=新的Child();
c121.Id=“c121”;
c121.Isleaf=true;
c12.Children=newlist(){c121};
c1.Children=newlist(){c11,c12};
父级p=新父级();
p、 Id=“P1”;
p、 Children=新列表(){c1,c2};

从上面的集合中,我想获取叶节点为true的所有子级的列表,即list leafNode=new list{c2,c11,c21}

首先,我将IsLeaf实现为

    public bool Isleaf
    {
        get
        {
            return Children.Any();
        }
    }
其次,我将有一个包含所有节点的第二个集合,以便轻松地跨树中的所有节点进行平面查询。您可以创建另一个名为RootNode的类。。。拥有一个属性AllNodes。然后你可以做

var leafNodes = rootNode.AllNodes.Where(a => a.Isleaf);
Telerik ASP.NET RadTreeNode控件正是为了让生活更轻松


我不建议尝试用lambda表达式来解决这个问题。递归方法可能适合:

void FindLeaves(Parent p, ICollection<Child> leaves)
{
    if (p.Children != null)
        foreach (var child in p.Children)
        {
            if (child.Isleaf)
                leaves.Add(child);
            FindLeaves(child, leaves);
        }
}

var leaves = new List<Child>();
FindLeaves(p, leaves);
如果以下任何一种不常见的情况为真,则递归解决方案可能不适用:

  • 在您的树中有循环的机会(例如ChildA->ChildB-> 奇尔达)。(堆栈溢出,除非添加了避免循环的混乱逻辑)
  • 这棵树可能非常深。(堆栈溢出)
  • 这棵树非常大,性能绝对至关重要
试试这个:

Get the children for each parent that has Isleaf = true;

var child1 = Parent.Children.FirstOrDefault(a => a.Isleaf);  // result is c2
var child2 = c12.Children.FirstOrDefault(a => a.Isleaf);     // result is c121
var child3 = c1.Children.FirstOrDefault(a => a.Isleaf);      // result is c11

List leafNode=new List {child1 ,child2 ,child3 };
不,只有当您有这个父->子结构时,这才有效。如果添加更多的子项,则需要有foreach循环。我这样做的原因是,我不知道在向家长添加孩子时,您试图建立的联系是什么。否则,如果您的所有子项都在父项-子项列表属性中。您可以简单地拥有一个foreach循环

公共静态类serctree
public static class SearcTree
{
    public static IEnumerable<T> GetLeaf<T>(this T rootNode, Func<T, IEnumerable<T>> childrenFunc)
    {
        var childrens = childrenFunc(rootNode);
        var haschild = childrens != null && childrens.Any();
        if (!haschild)
            yield return rootNode;
        else
            foreach (var node in childrenFunc(rootNode))
            {
                foreach (var child in GetLeaf(node, childrenFunc))
                {
                    childrens = childrenFunc(child);
                    haschild = childrenFunc(child) != null && childrens.Any();
                    if (!haschild)
                        yield return child;
                }
            }
    }
}



  //Uses: 
     var allLeaf = p.GetLeaf(root => root.Children);
{ 公共静态IEnumerable GetLeaf(此T根节点,Func childrenFunc) { var childrens=childrenFunc(rootNode); var haschild=childrens!=null&&childrens.Any(); 如果(!haschild) 产生返回根节点; 其他的 foreach(childrenFunc中的var节点(rootNode)) { foreach(GetLeaf中的变量child(节点,childrenFunc)) { childrens=childrenFunc(child); haschild=childrenFunc(child)!=null&&childrens.Any(); 如果(!haschild) 退换子女; } } } } //用途: var allLeaf=p.GetLeaf(root=>root.Children);
此解决方案基于Igby Largeman,但它使用堆栈并删除递归以防止堆栈溢出:

void FindLeaves(Parent p, ICollection<Child> leaves)
{
    if (p.Children != null) return;

    var toVisit = new Stack<Child>(p.Children());

    while (toVisit.Count > 0) {
        var current = toVisit.Pop(); 

        foreach (var child in current.Children)
        {    
            if (child.Isleaf)
                leaves.Add(child);
            else
                toVisit.Push(child);
        }
    }
}
void FindLeaves(父级p、ICollection叶)
{
如果(p.Children!=null)返回;
var toVisit=新堆栈(p.Children());
而(toVisit.Count>0){
var current=toVisit.Pop();
foreach(current.Children中的变量child)
{    
if(子岛)
叶。添加(子);
其他的
推(小孩);
}
}
}

我已经基于的解决方案编写了一个通用解决方案。这适用于对象包含自身集合的对象树。下面是一个简单的类来演示

public class Thing
{
    public IEnumerable<Thing> Children { get; set; }
    public bool IsLeaf => Children == null || !Children.Any();
}
这很好,因为它在Linq语句中工作。因此,如果您有一个
东西的集合
,那么您可以轻松地遍历树以找到集合中的所有叶子。下面是调用此扩展方法的过程

var leaves = things.Leaves(t => t.Children, t => t.IsLeaf);

things
things
IEnumerable
。最简单的方法是创建一个包含大量
东西的
新列表()
,并将其分配给
东西。
Leaves
的参数是遍历树所需的两个属性。第一个是
子节点
,它们是树的子节点。第二个属性是
IsLeaf
,它告诉我们这个
东西是否是叶子。此调用返回一个
IEnumerable
,其中只包含
things
中的叶子。如果需要,使用
.ToList()
将其添加到列表中。我不认为它能变得比那简单得多

更好。。。公共静态ICollection FindLeaves(此父级p){var leaves=new List();if(p.Children!=null)foreach(p.Children中的var child){if(child.Isleaf)leaves.Add(child);else leaves.AddRange(FindLeaves(child));}}小心!根据现实世界中的结构,递归算法可能会导致堆栈溢出。如果你说的是一个巨大的结构,我会写它recursion@Mick:如果树中存在循环,则可能出现堆栈溢出,是。答案经过编辑以反映这一点并不需要无限循环,只需要树的深度足够大,以便在执行代码时耗尽堆栈。关键是,它不是无限的,它取决于你所传递的内容的大小,但是在kapow之前你可能会得到15000-30000个递归。再加上非递归算法性能更好,即使它不会崩溃,实现任何递归算法都会增加权重。我只在绝对知道迭代次数很小的情况下使用递归,即100次迭代。你能走多深?。。。上述扩展方法有助于获取所有叶节点根节点。
public static class EnumerableExtensions
{
    public static IEnumerable<TSource> Leaves<TSource>(this IEnumerable<TSource> source, Func<TSource, IEnumerable<TSource>> children, Func<TSource, bool> isLeaf)
    {
        var nodes = new Stack<TSource>(source);
        while (nodes.Any())
        {
            var current = nodes.Pop();
            if(isLeaf(current))
            {
                yield return current;
                continue;
            }
            foreach (var child in children(current))
            {
                if (isLeaf(child))
                {
                    yield return child;
                }
                else
                {
                    nodes.Push(child);
                }
            }
        }
    }
}
var leaves = things.Leaves(t => t.Children, t => t.IsLeaf);