2010-02-27 10 views
6

Angenommen wir einige denormalisierter Daten haben, wie folgt aus:Normalisieren von Daten mit 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); 

ich es normalisieren müssen, z.B. IEnumerable erhalten < Child> mit Child.Parent und Child.Parent.GrandParent gefüllt.

Imperative Art ist mehr oder weniger klar. Wird es mit Linq kürzer sein?

Besser in einer Abfrage, und dies sollte für weitere Entitäten erweiterbar sein.

Ich habe versucht, so etwas wie separat erstellen IEnumerable < Großeltern>, dann IEnumerable < Parent> mit usw. Zuordnung

bitte einen Hinweis macht dies in einer funktionalen Weise erreicht werden könnte?

+0

Haben Sie Groupby() versucht? –

+0

Problem ist, wie Sie Entitäten ohne Duplikate erstellen und verknüpfen. Auswählen (neues Elternteil {GrandParent = neues GrandParent}) Oder ich vermisse etwas? – rudnev

Antwort

0

Linq macht wirklich das Gegenteil davon. dh. Wenn Sie es normalisiert hätten, könnten Sie leicht sagen

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 }; 

Zu tun, was Sie fragen, ist schwieriger. So etwas wie das

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

Ich bin nicht überzeugt, dass dies besser liest als die imperative Version würde.

0

Die einfachste Art und Weise, dies zu tun mit anonymen Variablen wäre:

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] 
     } 
}; 

Wenn Sie mit dieser konkreten Klassen tun wollte, würde ich vorschlagen, eine Person Klasse mit einem Konstruktor zu schaffen, die eine IEnumerable<Person>, die die Kinder nimmt von der Person wird konstruiert. Dann könnten Sie das tun:

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

Funktioniert eine dieser Lösungen für Sie?

Wenn Sie verschiedene GrandParent, Parent & Child Typen dann sollten Sie wollen in der Lage sein, das letzte Beispiel zu modifizieren, anzupassen.

1

Mit Gruppen können Sie genau das machen, was Sie wollen. Leider ist meine Kenntnis der C# LINQ-Syntax begrenzt, daher kann ich Ihnen nur die Art und Weise zeigen, wie die Erweiterungsmethode GroupBy aufgerufen wird.

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