2010-02-27 3 views
6

Supposons que nous ayons des données dénormalisées, comme ceci:données avec LINQ Normaliser

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

J'ai besoin de normaliser, par exemple pour obtenir IEnumerable < Enfant> avec Child.Parent et Child.Parent.GrandParent rempli.

La manière impérative est plus ou moins claire. Est-ce que ce sera plus court avec Linq?

Mieux dans une requête, et cela devrait être extensible pour plusieurs entités.

J'ai essayé quelque chose comme créer séparément IEnumerable < grand-parent>, puis IEnumerable < Parent> avec l'attribution, etc.

assurez un indice pourrait-il être atteint d'une manière fonctionnelle?

+0

Avez-vous essayé Groupby()? –

+0

Problème est comment créer et lier des entités sans doublons après. Sélectionnez (nouveau Parent {GrandParent = nouveau GrandParent}) Ou quelque chose me manque? – rudnev

Répondre

0

Linq fait vraiment le contraire de cela. c'est à dire. Si vous l'aviez normalisé, vous pouvez facilement 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 }; 

Pour faire ce que vous demandez est plus délicate. Quelque chose comme ça

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

Je ne suis pas convaincu que cela se lit mieux que la version impérative.

0

La façon la plus simple de le faire serait avec des variables anonymes:

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 vous voulez faire avec des classes concrètes je suggère la création d'une classe Person avec un constructeur qui prend un IEnumerable<Person> représentant les enfants du Person en cours de construction. Alors vous pourriez faire ceci:

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

Est-ce que l'une ou l'autre de ces solutions fonctionne pour vous?

Si vous voulez différents GrandParent, Parent & Child types, alors vous devriez être en mesure de modifier le dernier exemple en fonction.

1

Vous pouvez faire exactement ce que vous voulez en utilisant grouper par. Malheureusement ma connaissance de la syntaxe C# LINQ est limitée, donc je peux juste vous montrer comment appeler la méthode d'extension 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); 
    } 
}