Нормализуйте данные с LINQ

Предположите, что у нас есть некоторые денормализованные данные, как это:

List<string[]> dataSource = new List<string[]>();
string [] row1 = {"grandParentTitle1", "parentTitle1", "childTitle1"}; 
string [] row2 = {"grandParentTitle1", "parentTitle1", "childTitle2"};
string [] row3 = {"grandParentTitle1", "parentTitle2", "childTitle3"};
string [] row4 = {"grandParentTitle1", "parentTitle2", "childTitle4"};
dataSource.Add(row1);

Я должен нормализовать его, например, получить IEnumerable <Ребенок> с Ребенком. Родитель и Ребенок. Родитель. GrandParent заполняется.

Обязательный путь более или менее ясен. Это будет короче с Linq?

Лучше в одном запросе, и это должно быть расширяемо для большего количества объектов.

Я попробовал что-то, любят, отдельно создают IEnumerable <Прародитель>, затем IEnumerable <Родитель> с присвоением и т.д.

Сделайте подсказку, это могло быть достигнуто функциональным способом?

6
задан rudnev 27 February 2010 в 20:50
поделиться

3 ответа

Linq действительно делает противоположное. т.е. Если бы вы его нормализовали, вы могли бы легко сказать

from g in grandParents
from p in g.Parents
from c in p.Children
select new { GrandParentName = g.Name, ParentName = p.Name, ChildName = c.Name };

. Сделать то, о чем вы просите, сложнее. Что-то вроде этого

var grandparents = (from g in dataSource
                    select new GrandParent {
                        Title = g[0],
                        Parents = (from p in dataSource
                                   where p[0] == g[0]
                                   select new Parent {
                                      Title = p[1],
                                      Children = from c in dataSource
                                                 where p[1] == c[1]
                                                 select new
                                                            {
                                                                Title = c[2]
                                                            }
                                   }).Distinct(new ParentTitleComparer())
                    }).Distinct(new GrandParentTitleComparer());

Я не уверен, что это читается лучше, чем императивная версия.

0
ответ дан 17 December 2019 в 22:12
поделиться

Самый простой способ сделать это - использовать анонимные переменные:

from ds0 in dataSource group ds0 by ds0[0] into grandparents
select new
{
    Grandparent = grandparents.Key,
    Parents =
        from ds1 in grandparents group ds1 by ds1[1] into parents
        select new
        {
            Parent = parents.Key, 
            Children = from ds2 in parents select ds2[2]
        }
};

Если вы хотите сделать это с конкретными классами, я бы предложил создать Person класс с конструктором, который принимает IEnumerable , представляющий потомков конструируемого Person . Тогда вы можете сделать следующее:

from ds0 in dataSource
group ds0 by ds0[0] into grandparents
select new Person(grandparents.Key,
    from ds1 in grandparents
    group ds1 by ds1[1] into parents
    select new Person(parents.Key,
        from ds2 in parents
        select new Person(ds2[2])));

Подходит ли вам одно из этих решений?

Если вам нужны разные типы GrandParent , Parent и Child , тогда у вас должна быть возможность изменить последний пример в соответствии с вашими требованиями.

0
ответ дан 17 December 2019 в 22:12
поделиться

Вы можете делать именно то, что хотите, используя group by. К сожалению, мои знания синтаксиса C # LINQ ограничены, поэтому я просто могу показать вам способ вызова метода расширения GroupBy.

var normalized = dataSource
    .GroupBy(source => source[0], (grandParent, grandParentChilds) => new { GrandParent = grandParent, Parents = grandParentChilds
        .GroupBy(source => source[1], (parent, parentChilds) => new { Parent = parent, Children = from source in parentChilds select source[2]}) });

foreach (var grandParent in normalized)
{
    Console.WriteLine("GrandParent: {0}", grandParent.GrandParent);
    foreach (var parent in grandParent.Parents)
    {
        Console.WriteLine("\tParent: {0}", parent.Parent);
        foreach (string child in parent.Children)
            Console.WriteLine("\t\tChild: {0}", child);
    }
}
1
ответ дан 17 December 2019 в 22:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: