簡體   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