我试图使用linq获得满足这些条件的所有菜单和子程序:
这是菜单类:
public class Menu
{
public string Name { get; set; }
public string Link { get; set; }
public List<Menu> Children { get; set; }
public Menu()
{
Children = new List<Menu>();
}
}假设我们有这样的数据结构:
List<Menu> root = new List<Menu>();
Menu parent_1 = new Menu() { Name = "Parent 1", Link = null };
Menu parent_2 = new Menu() { Name = "Parent 2", Link = null };
//children for parent 1
Menu p1_child_1 = new Menu() { Name = "p1_child_1", Link = null };
Menu p1_child_2 = new Menu() { Name = "p1_child_2", Link = null };
//sub children of p1_child_2
Menu p1_child_1_1 = new Menu() { Name = "p1_child_1_1", Link = "l1-1" };
Menu p1_child_1_2 = new Menu() { Name = "p1_child_1_2", Link = null };
p1_child_1.Children.AddRange(new List<Menu> { p1_child_1_1 , p1_child_1_2 });
parent_1.Children.AddRange(new List<Menu> { p1_child_1, p1_child_2 });
Menu p2_child_1 = new Menu() { Name = "p2_child_1", Link = null };
Menu p2_child_2 = new Menu() { Name = "p2_child_2", Link = "l2-2" };
Menu p2_child_1_1 = new Menu() { Name = "p2_child_1_1", Link = null };
Menu p2_child_1_2 = new Menu() { Name = "p2_child_1_2", Link = null };
p2_child_1.Children.AddRange(new List<Menu> { p2_child_1_1, p2_child_1_2 });
parent_2.Children.AddRange(new List<Menu> { p2_child_1, p2_child_2 });
root.Add(parent_1);
root.Add(parent_2);结果:基于所请求的条件返回的筛选列表将是:
parent_1
- p1\_child\_1\_1
parent_2
如何使用Linq或替代方法来实现这一点,同时考虑到菜单可以达到多个级别?
尝试了注释中提出的解决方案,我添加了扩展方法
public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source, Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
{
if (source == null)
{
yield break;
}
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
foreach (var childItem in childResults)
{
yield return childItem;
}
}然后调用该方法:
var result = root.SelectHierarchy(n => n.Children, n => n.Children.Count > 0 || n.Link != null).ToList();然而,这不是我想要的,我希望两个菜单携带的subMenus满足我的条件,但我得到了6个菜单,我想是扁平的。

虽然,由于子代计数> 0,所以返回了p2_child_1,但是它不应该导致菜单没有链接。(我将谓词放在上面,因为我没有其他选项。
发布于 2017-01-09 23:42:41
这对我来说很管用:
public static class Ex
{
public static List<Menu> CloneWhere(this List<Menu> source, Func<Menu, bool> predicate)
{
return
source
.Where(predicate)
.Select(x => new Menu()
{
Name = x.Name,
Link = x.Link,
Children = x.Children.CloneWhere(predicate)
})
.Where(predicate)
.ToList();
}
}示例数据如下所示:

...then我可以应用以下内容:
var result = root.CloneWhere(m => m.Children.Any() || m.Link != null);...and我得到了这样的结果:

发布于 2017-01-10 00:06:55
假设深度不太大导致堆栈溢出,则可以使用简单的递归方法或递归lambda,如下所示:
Func<List<Menu>, List<Menu>> filter = null;
filter = items =>
(from item in items
let children = filter(item.Children)
where item.Link != null || children.Any()
select new Menu { Name = item.Name, Link = item.Link, Children = children }
).ToList();
var filtered = filter(root);关键部分是在过滤父级之前先处理子级(后置顺序遍历)。
https://stackoverflow.com/questions/41556264
复制相似问题