Linq-to-objects: создание двухуровневой иерархии из плоского источника - PullRequest
4 голосов
/ 01 июня 2011

Допустим, у меня есть эта простая структура

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

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

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

A Bar имеет список 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 }
                           }};

Я хотел бы получить коллекцию предметов верхнего уровня с их детьми.Соответствующая структура данных, вероятно, будет IEnumerable<IGrouping<Foo, Foo>>.

Результат будет выглядеть следующим образом:

  • Элемент 1 (простой)
  • Элемент 2 (родительский)
    • Item 3 (child)

И, конечно, я хотел бы сделать это с помощью чисто функционального запроса Linq.Я делаю много из них, но мой мозг сегодня застрял.

Ответы [ 2 ]

3 голосов
/ 01 июня 2011
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 из Tuple содержит дочерние элементы для родителя в Item1.Для вашего примера это возвращает два кортежа:

  • Item1 = simpleDefinition и Item2, содержащий пустой перечисляемый
  • Item1 = 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>>>.

Обновление:
Я хотел бы знать, если решениеВы хотели, возможно, вообще.
Да, это:

bar.Foos.Where(x => x.Definition.Parent != null)
        .GroupBy(x => bar.Foos.Where(y => y.Definition == x.Definition.Parent)
                              .Single(),
                 x => x)
        .Union(bar.Foos.Where(x => x.Definition.Parent == null && 
                                   !bar.Foos.Any(c => c.Definition.Parent == 
                                                      x.Definition))
                       .GroupBy(x => x, x => (Foo)null));

Но я действительно не хочу знать большой O этого, и это действительно не должноиспользоваться; -)

0 голосов
/ 01 июня 2011

Если вы добавите класс и метод, вы можете получить значение 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);
...