Comment aplatir des objets imbriqués avec une expression linq

125

J'essaye d'aplatir les objets imbriqués comme ceci:

public class Book
{
    public string Name { get; set; }
    public IList<Chapter> Chapters { get; set; }
}

public class Chapter
{
    public string Name { get; set; }
    public IList<Page> Pages { get; set; }
}


public class Page
{
    public string Name { get; set; }
}

Laissez-moi faire un exemple. Ce sont les données que j'ai

Book: Pro Linq 
{ 
   Chapter 1: Hello Linq 
   {
      Page 1, 
      Page 2, 
      Page 3
   },
   Chapter 2: C# Language enhancements
   {
      Page 4
   },
}

Le résultat que je recherche est la liste plate suivante:

"Pro Linq", "Hello Linq", "Page 1"
"Pro Linq", "Hello Linq", "Page 2"
"Pro Linq", "Hello Linq", "Page 3"
"Pro Linq", "C# Language enhancements", "Page 4"

Comment pourrais-je accomplir cela? Je pourrais le faire avec un nouveau select mais on m'a dit qu'un SelectMany suffirait.

abx78
la source

Réponses:

199
myBooks.SelectMany(b => b.Chapters
    .SelectMany(c => c.Pages
        .Select(p => b.Name + ", " + c.Name + ", " + p.Name)));
Yuriy Faktorovich
la source
Impressionnant!!! Et si j'aurais comme résultat un nouvel objet, comme FlatBook {BookName, ChapterName, PageName}?
abx78 le
2
@ abx78: modifiez simplement la dernière sélection:.Select(p => new FlatBook(b.Name, c.Name, p.Name))
user7116
Merci les gars, c'est ce dont j'ai besoin!
abx78
1
cela produit-il le même résultat? myBooks.SelectMany(b => b.Chapters).SelectMany(c => c.Pages).Select(p => b.Name + ", " + c.Name + ", " + p.Name);
Homer
1
@Mastro how aboutmyBooks.SelectMany(b => b.Chapters == null || !b.Chapters.Any()? new []{b.Name + " has no Chapters"} : b.SelectMany(c => c.Pages.Select(p => b.Name + ", " + c.Name + ", " + p.Name)));
Yuriy Faktorovich
50

En supposant booksune liste de livres:

var r = from b in books
    from c in b.Chapters
    from p in c.Pages
    select new {BookName = b.Name, ChapterName = c.Name, PageName = p.Name};
boca
la source
2
+1, même si n'importe lequel IEnumerable<Book>fera l'affaire, n'a pas besoin d'un List<Book>.
user7116
2
myBooks.SelectMany(b => b.Chapters
    .SelectMany(c => c.Pages
        .Select(p => new 
                {
                    BookName = b.Name ,
                    ChapterName = c.Name , 
                    PageName = p.Name
                });
Madhukar Bhandari
la source
7
Bien que cet exemple de code puisse répondre à la question, il manque d'explication. Dans l'état actuel des choses, il n'ajoute aucune valeur et résiste au changement de vote défavorable / supprimé. Veuillez ajouter quelques explications sur ce que fait et pourquoi c'est une solution au problème du PO.
oɔɯǝɹ
0

J'essayais de le faire aussi, et d'après les commentaires de Yuriy et le fait de jouer avec linqPad, j'ai ceci ...

Notez que je n'ai pas de livres, chapitres, pages, j'ai personne (livres), entreprisePersonne (chapitres) et entreprises (pages)

from person in Person
                           join companyPerson in CompanyPerson on person.Id equals companyPerson.PersonId into companyPersonGroups
                           from companyPerson in companyPersonGroups.DefaultIfEmpty()
                           select new
                           {
                               ContactPerson = person,
                               ContactCompany = companyPerson.Company
                           };

ou

Person
   .GroupJoin (
      CompanyPerson, 
      person => person.Id, 
      companyPerson => companyPerson.PersonId, 
      (person, companyPersonGroups) => 
         new  
         {
            person = person, 
            companyPersonGroups = companyPersonGroups
         }
   )
   .SelectMany (
      temp0 => temp0.companyPersonGroups.DefaultIfEmpty (), 
      (temp0, companyPerson) => 
         new  
         {
            ContactPerson = temp0.person, 
            ContactCompany = companyPerson.Company
         }
   )

Site de référence que j'ai utilisé: http://odetocode.com/blogs/scott/archive/2008/03/25/inner-outer-lets-all-join-together-with-linq.aspx

Mastro
la source