Нормализуйте данные с помощью LINQ - PullRequest
6 голосов
/ 27 февраля 2010

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

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 с заполненными Child.Parent и Child.Parent.GrandParent.

Императивный путь более или менее понятен. Будет ли это короче с Linq?

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

Я пробовал что-то вроде отдельного создания IEnumerable , затем IEnumerable с назначением и т. Д.

Пожалуйста, дайте намек, можно ли это сделать функционально?

Ответы [ 3 ]

1 голос
/ 04 марта 2010

Вы можете делать именно то, что вы хотите, используя группу по. К сожалению, мои знания синтаксиса 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);
    }
}
0 голосов
/ 02 марта 2010

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

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>, представляющий потомков конструируемого 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 голосов
/ 28 февраля 2010

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

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());

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

...