层次结构迭代与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的决定,我会选择它作为答案