2010-02-27 11 views
6

Supponiamo di avere alcuni dati denormalizzati, in questo modo:dati Normalizzare con 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); 

ho bisogno di normalizzare esso, per esempio per ottenere IEnumerable < Child> con Child.Parent e Child.Parent.GrandParent riempiti.

La via imperativa è più o meno chiara. Sarà più breve con Linq?

Meglio in una query, e questo dovrebbe essere espandibile per più entità.

ho provato qualcosa di simile a parte creare IEnumerable < Nonni>, quindi IEnumerable Parent <> con l'assegnazione ecc

si prega di prendere suggerimento potrebbe questo essere raggiunto in modo funzionale?

+0

Hai provato Groupby()? –

+0

Problema: come creare e collegare entità senza duplicati dopo. Seleziona (nuovo genitore {GrandParent = new GrandParent}) O mi manca qualcosa? – rudnev

risposta

0

Linq fa davvero l'opposto di questo. vale a dire. Se lo avessi normalizzato, potresti facilmente dire

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

Fare ciò che stai chiedendo è più complicato. Qualcosa di simile

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

Non sono convinto che questo si legge meglio della versione imperativa.

0

Il modo più semplice di fare questo sarebbe con le variabili anonimi:

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

Se si voleva fare questo con classi concrete vorrei suggerire la creazione di una classe di Person con un costruttore che prende un IEnumerable<Person> che rappresenta i bambini di Person in corso di costruzione. Quindi è possibile effettuare questa operazione:

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

Una di queste soluzioni funziona per voi?

Se si vuole diversa GrandParent, Parent & Child tipi allora si dovrebbe essere in grado di modificare l'ultimo esempio per adattarsi.

1

È possibile eseguire esattamente ciò che si desidera utilizzando per gruppo. Sfortunatamente la mia conoscenza della sintassi C# LINQ è limitata, quindi posso solo mostrarti come chiamare il metodo di estensione 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); 
    } 
}