Linq-to-objects:从平面源创建两级层次结构

时间:2011-06-01 15:18:10

标签: c# linq-to-objects

假设我有这个简单的结构

class FooDefinition
{
    public FooDefinition Parent { get; set; }
}

class Foo
{
    public FooDefinition Definition { get; set; }
}

class Bar
{
    public ICollection<Foo> Foos { get; set; }
}

Bar的{​​{1}}列表可以很简单(没有父/子关系)或只嵌套一个级别(即父Foos有多个孩子Foo })。从这里可以看出,关系是在Foos中指定的,而不是FooDefinition

我需要做的是生成一个按此层次结构正确分组的Foo itself列表。请考虑以下源数据:

Foos

我想和他们的孩子一起收集顶级物品。一个适当的数据结构可能是var simpleDefinition = new FooDefinition(); var parentDefinition = new FooDefinition(); var childDefinition = new FooDefinition { Parent = parentDefinition }; var bar = new Bar { Foos = new[] { new Foo { Definition = simpleDefinition }, new Foo { Definition = parentDefinition }, new Foo { Definition = childDefinition } }};

结果如下:

  • 第1项(简单)
  • 第2项(父母)
    • 第3项(儿童)

当然,我想用纯功能的Linq查询来做到这一点。我做了很多这些,但我的大脑今天似乎停滞不前。

2 个答案:

答案 0 :(得分:3)

bar.Foos.Where(x => x.Definition.Parent == null)
        .Select(x => Tuple.Create(x, 
                                  bar.Foos.Where(c => c.Definition
                                                       .Parent == x.Definition
                                                ))); 

这将返回IEnumerable<Tuple<Foo, IEnumerable<Foo>>>,其中Item2的{​​{1}}包含Tuple中父项的子项。对于您的示例,这将返回两个元组:

  • Item1Item1 = simpleDefinition包含空的可枚举
  • Item2Item1 = parentDefinition包含一个包含Item2
  • 的枚举

可能有更优雅或更快的方式,但我无法想出来......

哦,好吧,我对此我的评论有点矛盾,但是childDefinition有可能 - 至少差不多:

GroupBy

这将返回bar.Foos.Where(x => x.Definition.Parent == null) .GroupBy(x => x, x => bar.Foos.Where(c => c.Definition.Parent == x.Definition));

更新:
我想知道,如果您想要的解决方案是可能的。
是的,它是:

IEnumerable<IGrouping<Foo, IEnumerable<Foo>>>

但我真的不想知道大O 这个并且它真的不应该被使用; - )

答案 1 :(得分:0)

如果您添加了一个类和一个方法,则可以转到IEnumerable<IGrouping<Foo,Foo>>

    class FooRelation{
        public Foo Parent {get; set;}
        public Foo Child  {get; set;}
    }

    static IEnumerable<FooRelation> GetChildren(Bar source, Foo parent){
        var children = source.Foos.Where(c => c.Definition.Parent == parent.Definition);
        if(children.Any())
            return children.Select(c => new FooRelation{Parent = parent, Child = c});
        return new FooRelation[]{new FooRelation{Parent = parent}};
    }

您甚至可以将该静态方法折叠到此查询中......但它会变得混乱:

     var r = bar.Foos.Where(x => x.Definition.Parent == null)
                .SelectMany(x => GetChildren(bar, x))
                .GroupBy(fr => fr.Parent, fr => fr.Child);