分层结构迭代和LINQ

时间:2009-12-23 09:45:33

标签: linq hierarchical-data

假设我们有课

public class RMenuItem
{
    public List<RMenuItem> ChildrenItems { get; }
    public decimal OperationID { get; }
    public string Name { get; }
}

正如您所看到的 - 每个菜单项都可以包含子项 - 与菜单中一样。 我的任务是遍历此列表中的每个项目并对其应用一些操作。经典决策是编写递归迭代。但是,如果LINQ可以让我的任务变得更容易,我会感兴趣吗?例如,我想我们可以编写可以得到平面对象列表的查询,我可以使用foreach简单地迭代。但我这样做的尝试还没有成功。 所以任何帮助表示赞赏!

4 个答案:

答案 0 :(得分:4)

可能

    public void PrintAllNames(RMenuItem rootItem)
    {
        Action<RMenuItem> print = null;
        print = m =>
            {
                Console.WriteLine(m.Name);
                m.ChildrenItems.ForEach(print);
            };
        print(rootItem);
    }

请注意如何声明print以便print可以自行使用。这与递归方法直接相当,我宁愿使用

    public void PrintAllNames(RMenuItem rootItem)
    {
        Console.WriteLine(rootItem.Name);
        rootItem.ChildrenItems.ForEach(PrintAllNames);
    }

(虽然对于更复杂的情况,可能功能解决方案最有意义)

答案 1 :(得分:1)

事实上,您可以使用LINQ,SelectMany列出列表,只是一些示例

menuItemsList.SelectMany(x => x.ChildrenItems).Where(c => c.someChildProperty);

由于

修改

在回应评论时,我刚才给出了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 
             });

基本上你可以在{}

中做你想要的元素

由于

答案 2 :(得分:1)

我建议实现这两种方法。您可以使用实用程序方法来获取所有项目,或者可以实现Visitor Pattern,但这意味着更改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());
        }

答案 3 :(得分:1)

您可以在类中定义Flatten方法(如果您愿意,也可以作为扩展名),如此

public IEnumerable<RMenuItem> Flatten()
{
    foreach (var item in ChildrenItems)
    {
        yield return item;
    }
    return ChildrenItems.SelectMany(item => item.Flatten());
}

然后对每个元素做一些事情就像

一样简单
RMenuItem rootItem ;

    // do somthing with the root item
    foreach (var item  in rootItem.Flatten())
    {
        // do somthing
    }