递归Lambda表达式查询
本文关键字:查询 表达式 Lambda 递归 | 更新日期: 2023-09-27 18:01:02
我正试图编写一个递归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表达式,为所有子级和子级设置checked。请帮忙。
如果不可能的话,我也愿意接受其他解决方案。
不能仅使用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
是一个递归函数,它检查节点并为其所有子节点调用自己
CCD_ 2只是一个通过给定的ID列表为每个节点调用CCD_ 3的函数。
例如,您可以将它封装在一个公共的静态助手类中,使用它会很方便:
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演示。
p.S。注意,我使用的是LINQ Single
函数,如果集合中没有这样的元素,它将引发异常。例如,如果您调用nodes.CheckNodes(11)
,并且没有Id
=11的Node
,它将抛出异常。当然,您可以在CheckNodes
中添加一个是否存在的检查,但如果您确信只通过现有的Id
s,那么它是多余的。这就是我没有使用它的原因。
也许这就是您想要的:
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);
对不起,我不知道您类型的声明包含NodeId
、Parent
和IsChecked
。所以我有点猜测。
也许您可以为IEnumerable<T>
添加一个扩展方法,使其具有ForEach
,并删除创建集合副本的ToList
调用。
您可以编写where子句,如下所示:
nodes.Where(node =>
checkedNodes.Contains(node.Id) ||
(node.ParentId != null && checkedNodes.Contains((int)node.ParentId))
).ToList().ForEach(each => { each.IsChecked = true; });
看看完整的演示示例这里
这不是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进行排序,以便在子节点之前到达父节点。还要注意,我正在将新找到的父母添加到包中。