递归Lambda表达式查询

时间:2015-07-03 05:14:44

标签: c# recursion lambda

我正在尝试编写一个递归的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表达式来为所有子项和子项设置检查。请帮助。

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

4 个答案:

答案 0 :(得分:3)

您无法仅使用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 Demo

P.S 。注意,我正在使用LINQ Single函数,如果集合中没有这样的元素,它将抛出异常。例如,如果您致电nodes.CheckNodes(11)并且Node = 11没有Id,则会抛出异常。当然,您可以在CheckNodes中添加对存在的检查,但如果您确定只传递现有的Id,则这是多余的。这就是我没有使用它的原因。

答案 1 :(得分:2)

也许这就是你要找的东西:

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来电。

答案 2 :(得分:1)

这不是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进行排序,以便在孩子之前联系父母。另请注意,我正在将新发现的父母添加到包中。

答案 3 :(得分:1)

你可以写where where这样的子句:

 function createList($main_topics)
        {
            if($main_topics == null || sizeof($main_topics) <= 0)
            {
                return '';
            }

            $list = '<ul>';
            foreach($main_topics as $k_main_topics => $v_main_topics )
            {

                $list .= '<li id="' . $k_main_topics'"> '. $v_main_topics['name'] . ' ' .  createList(isset($v_main_topics["children"]) ? $v_main_topics["children"] : null) . '</li>' ;

            }

            $list .= '</ul>';

            return $list;

        }

        echo createList($main_array);

查看完整的演示示例Here