[英]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)
和將沒有Node
與Id
= 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);
對不起,我不知道您的包含NodeId
, Parent
和IsChecked
類型的聲明。 所以我有點猜測。
也許您可以為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.