层次结构迭代与LINQ
假设我们有课层次结构迭代与LINQ,linq,hierarchical-data,Linq,Hierarchical Data,假设我们有课 public class RMenuItem { public List<RMenuItem> ChildrenItems { get; } public decimal OperationID { get; } public string Name { get; } } 公共类RMenuItem { 公共列表子项{get;} 公共十进制操作ID{get;} 公共字符串名称{get;} } 正如您所看到的,每个菜单项都可以有子项,就像菜单中的
public class RMenuItem
{
public List<RMenuItem> ChildrenItems { get; }
public decimal OperationID { get; }
public string Name { get; }
}
公共类RMenuItem
{
公共列表子项{get;}
公共十进制操作ID{get;}
公共字符串名称{get;}
}
正如您所看到的,每个菜单项都可以有子项,就像菜单中的一样。
我的任务是遍历列表中的每个项目,并对其应用一些操作。经典的决策是编写递归迭代。但如果LINQ能让我的任务更简单,我很感兴趣?例如,我假设我们可以编写查询来获得对象的平面列表,我可以简单地使用foreach进行迭代。但我的这种尝试还没有成功。
因此,感谢您的帮助 这是可能的:
public void PrintAllNames(RMenuItem rootItem)
{
Action<RMenuItem> print = null;
print = m =>
{
Console.WriteLine(m.Name);
m.ChildrenItems.ForEach(print);
};
print(rootItem);
}
(尽管对于更复杂的情况,功能性解决方案可能最有意义)实际上,您可以使用LINQ来实现这一点,
选择many
将列表平铺,仅举一个例子
menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);
谢谢
编辑:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
作为对这些评论的回应,我之前只是举了一个SelectMany
的例子。谢谢你指出
menuItemsList.SelectMany(x => x.ChildrenItems.Select(p => p)).Where(c => c.someChildProperty);
或者类似的事情
menuItemsList.SelectMany(x => x.ChildrenItems).Select(p => p).Where(c => c.someChildProperty);
Edit2
啊。。现在我明白你想要什么了
我们只需稍微修改我上面的查询即可
menuItemsList
.SelectMany(x => { //do something with x like printing it
x.ChildrenItems
})
.Select(p => { // do something with p like printing it
p
});
基本上,您可以在{}
谢谢我建议两种方法来实现这一点。您可以选择使用实用程序方法获取所有项,也可以实现,尽管这意味着更改
RMenuItem
类
实用方法:
static IEnumerable<RMenuItem> GetAllMenuItems(IList<RMenuItem> items)
{
if (items == null)
throw new ArgumentNullException("items");
Queue<RMenuItem> queue = new Queue<RMenuItem>(items);
while (queue.Count > 0)
{
var item = queue.Dequeue();
if (item.ChildrenItems != null)
{
foreach (var child in item.ChildrenItems)
{
queue.Enqueue(child);
}
}
yield return item;
}
}
public interface IRMenuItemVisitor
{
void Visit(RMenuItem item);
}
public class PrintRMenuItemVisitor : IRMenuItemVisitor
{
public void Visit(RMenuItem item)
{
Console.WriteLine(item);
}
}
public interface IRMenuItem
{
void Accept(IRMenuItemVisitor visitor);
}
public class RMenuItem : IRMenuItem
{
// ...
public void Accept(IRMenuItemVisitor visitor)
{
visitor.Visit(this);
if (ChildrenItems != null)
{
foreach (var item in ChildrenItems)
{
item.Accept(visitor);
}
}
}
}
用法:
RMenuItem m1 = new RMenuItem
{
Name = "M1",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M11" },
new RMenuItem {
Name = "M12",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M121" },
new RMenuItem { Name = "M122" }
}
}
}
};
RMenuItem m2 = new RMenuItem
{
Name = "M2",
ChildrenItems = new List<RMenuItem> {
new RMenuItem { Name = "M21" },
new RMenuItem { Name = "M22" },
new RMenuItem { Name = "M23" }
}
};
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in GetAllMenuItems(menus))
{
Console.WriteLine(menu);
}
// or
IList<RMenuItem> menus = new List<RMenuItem> { m1, m2 };
foreach (var menu in menus)
{
menu.Accept(new PrintRMenuItemVisitor());
}
RMenuItem m1=新的RMenuItem
{
Name=“M1”,
ChildrenItems=新列表{
新RMenuItem{Name=“M11”},
新项目{
Name=“M12”,
ChildrenItems=新列表{
新RMenuItem{Name=“M121”},
新RMenuItem{Name=“M122”}
}
}
}
};
RMenuItem m2=新RMenuItem
{
Name=“M2”,
ChildrenItems=新列表{
新RMenuItem{Name=“M21”},
新RMenuItem{Name=“M22”},
新RMenuItem{Name=“M23”}
}
};
IList菜单=新列表{m1,m2};
foreach(GetAllMenuItems(菜单)中的变量菜单)
{
控制台写入线(菜单);
}
//或
IList菜单=新列表{m1,m2};
foreach(菜单中的变量菜单)
{
Accept(新的PrintRMenuItemVisitor());
}
您可以像这样在类中定义一个展平方法(或者作为扩展,如果您愿意的话)
public IEnumerable<RMenuItem> Flatten()
{
foreach (var item in ChildrenItems)
{
yield return item;
}
return ChildrenItems.SelectMany(item => item.Flatten());
}
这行不通。我也认为会,但你只会得到第一级菜单…这只会得到第一级子项。我认为andrey想要的是一个算法,它将访问所有的子项、孙子项等。where子句对this@Rob他实际上没有明确提到(至少我不清楚)他想要什么,所以我只是举一个例子,说明他如何利用Linq中的SelectMany。谢谢:)我已经提到,我需要遍历每个项目。这意味着我需要访问每一个孩子,直到层次结构的最后一级,或者关于linq查询-获取所有项目的平面列表。+1对于评论,请在向下投票时留下评论,以便作者知道帖子有什么问题,这会有所帮助。谢谢,我会尝试这个解决方案,但我开始觉得递归解决方案简单得多,直观得多。)谢谢所有gyus,但我觉得经典递归是唯一可以接受的决定,因为它的简单性。所以这不是使用LINQ的决定,我会选择它作为答案