2010-02-27 15 views
6

Asumamos que tenemos algunos datos no normalizados, como esto:datos Normalizar 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); 

tengo que normalizarla, por ejemplo, para obtener IEnumerable < Niño> con Child.Parent y Child.Parent.GrandParent lleno.

Manera imperativa es más o menos clara. ¿Será más corto con Linq?

Mejor en una consulta, y esto debería ser ampliable para más entidades.

he intentado algo así como crear por separado IEnumerable < Abuelos> y, a continuación IEnumerable < Padres> con la asignación etc.

Por favor, haga un indicio esto podría lograrse de una manera funcional?

+0

¿Has probado Groupby()? –

+0

El problema es cómo crear y vincular entidades sin duplicados después. Seleccionar (nuevo padre {GrandParent = new GrandParent}) ¿O extraño algo? – rudnev

Respuesta

0

Linq realmente hace lo contrario de esto. es decir. Si lo hubiera normalizado, podría decir fácilmente

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

Hacer lo que está pidiendo es más complicado. Algo como esto

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

No estoy seguro de que esto sea mejor que la versión imperativa.

0

La forma más básica de hacer esto sería con variables anónimas:

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

Si quería hacer esto con clases concretas que sugeriría la creación de una clase Person con un constructor que toma un IEnumerable<Person> que representa a los niños del Person en construcción. Entonces podría hacer esto:

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

¿Funciona alguna de estas soluciones para usted?

Si usted quiere diverso GrandParent, Parent & Child tipos, entonces debería ser capaz de modificar el último ejemplo para adaptarse.

1

Puedes hacer exactamente lo que quieras usando group by. Lamentablemente, mi conocimiento de la sintaxis de C# LINQ es limitado, por lo que puedo mostrarte cómo llamar al método de extensión 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); 
    } 
}