繁体   English   中英

递归Lambda表达式查询

[英]Recursive Lambda Expression Query

我试图通过以下结构编写一个递归的lambda表达式。

NodeID ParentNodeID IsChecked
  1        null        false
  2         1          false
  3         1          false
  4         1          false
  5         1          false
  6         2          false
  7         2          false
  8         2          false
  9         2          false
 10         6          false
 11         6          false
 12         6          false
 13         3          false
 14         3          false
 15         13         false
 16         13         false

//Now i have a List<Int32> checkedNodes Which has 2,3 in it.
List<Int32> checkedNodes = new List<Int32>({2,3});
//I can write a lambda expression which will set these nodes checked value to true.
myList.Where(x => checkedNodes.Contains(x.NodeID)).ToList().ForEach(x => x.Checked = true);

我想编写我的lambda表达式来设置所有子级和子级子级的检查。 请帮忙。

如果不可能的话,我也愿意接受替代解决方案。

您不能仅使用LINQ构建递归过程。
您将需要一个递归函数。

我认为这是一个合适且优雅的解决方案:

private static void Traverse(Node node)
{
    node.Checked = true; 
    _nodes.Where(x => x.ParentId == node.Id).ToList().ForEach(Traverse);
}

public static void Check(params int[] values)
{
    values.Select(item => _nodes.Single(x => x.Id == item)).ToList().ForEach(Traverse);
}

Traverse是一个递归函数,它检查节点并为其所有子节点调用自身。
Check只是通过给定ID列表为每个节点调用Traverse的函数。

例如,您可以将其包装在公共静态帮助器类中,使用起来会很方便:

public static class NodeRecursiveChecker
{
    private static List<Node> _nodes;
    private static void Traverse(Node node)
    {
        node.Checked = true; 
        _nodes.Where(x => x.ParentId == node.Id).ToList().ForEach(Traverse);
    }

    public static void CheckNodes(this List<Node> nodes, params int[] values)
    {
        _nodes = nodes;
        values.Select(item => _nodes.Single(x => x.Id == item)).ToList().ForEach(Traverse);
    }
}

然后,您可以通过以下方式使用它:

list.CheckNodes(6, 13); // Mark 6, 13 and their children
list.CheckNodes(1); // Mark everything (as 1 is a root in your case)

这是DotNetFiddle演示

PS 注意,我使用的是LINQ Single函数,如果集合中没有这样的元素,它将抛出异常。 例如,如果你调用nodes.CheckNodes(11)和将没有NodeId = 11 -它会抛出异常。 当然,您可以在CheckNodes添加是否存在检查,但是如果您确定仅传递现有Id ,则这是多余的。 这就是为什么我没有使用它。

也许这就是您想要的:

Func<Node, bool> predicate = null;
predicate = x => checkedNodes.Contains(x.NodeId) || (x.Parent != null && predicate(x.Parent));

myList.Where(predicate).ToList().ForEach(x => x.IsChecked = true);

对不起,我不知道您的包含NodeIdParentIsChecked类型的声明。 所以我有点猜测。

也许您可以为IEnumerable<T>添加扩展方法以拥有ForEach并删除创建您的集合副本的ToList调用。

这不是Linq,而是一种实现方式。 使用LInq的设计方式,您的问题应该是不可能的

List<int>  bag = new List<int>();
checkedNodes.ForEach(x=> bag.Add(x));

data.OrderBy(x=>x.ParentNodeId).ToList().ForEach(item=>{
    if(bag.Contains(item.ParentNodeId)){
        bag.Add(item.NodeId);
        item.IsChecked = true;
    }
});

该解决方案使用袋子保存已检查的所有父母的清单。 它效率不高,但适用于少量收藏。

请注意,我首先基于父节点ID进行排序,以在到达子节点之前先到达父节点。 另请注意,我正在将新发现的父母添加到书包中。

您可以编写where子句,如下所示:

nodes.Where(node => 
        checkedNodes.Contains(node.Id) || 
        (node.ParentId != null && checkedNodes.Contains((int)node.ParentId))
        ).ToList().ForEach(each => { each.IsChecked = true; });

这里看完整的演示示例

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM