How to flatten nested objects with linq expression

75,717

Solution 1

myBooks.SelectMany(b => b.Chapters
    .SelectMany(c => c.Pages
        .Select(p => b.Name + ", " + c.Name + ", " + p.Name)));

Solution 2

Assuming books is a List of Book:

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

Solution 3

myBooks.SelectMany(b => b.Chapters
    .SelectMany(c => c.Pages
        .Select(p => new 
                {
                    BookName = b.Name ,
                    ChapterName = c.Name , 
                    PageName = p.Name
                });
Share:
75,717
ab_732
Author by

ab_732

Updated on July 08, 2022

Comments

  • ab_732
    ab_732 almost 2 years

    I am trying to flatten nested objects like this:

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

    Let me make an example. This is the data I have

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

    The result I am looking for is the following flat list:

    "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"
    

    How could I accomplish this? I could do it with a select new but I've been told that a SelectMany would be enough.

  • Sam
    Sam almost 13 years
    +1, even though any IEnumerable<Book> will do, doesn't need a List<Book>.
  • ab_732
    ab_732 almost 13 years
    Awesome!!! What if I would have a result a new object, like FlatBook{BookName, ChapterName, PageName} ?
  • Sam
    Sam almost 13 years
    @abx78: simply alter the last select: .Select(p => new FlatBook(b.Name, c.Name, p.Name))
  • ab_732
    ab_732 almost 13 years
    Thank you guys, this was what I need!
  • Homer
    Homer about 11 years
    does this produce the same result? myBooks.SelectMany(b => b.Chapters).SelectMany(c => c.Pages).Select(p => b.Name + ", " + c.Name + ", " + p.Name);
  • The Muffin Man
    The Muffin Man almost 11 years
    So how would you rewrite this to support an N number of nested objects?
  • Mastro
    Mastro almost 10 years
    Is there a way to do this but include any Books that do not have Chapters?
  • Yuriy Faktorovich
    Yuriy Faktorovich almost 10 years
    @Mastro start it with myBooks.Where(b => b.Chapters != null)
  • Mastro
    Mastro almost 10 years
    Hmm well I wanted a list where both are shown. what you have here is where not null, I wanted a flatten list where I also, include the nulls but doesn't seem to work if I do Where(b => b.Chapters == null) as I get 0. Would like to have a flatten list with all books, ones with or without chapters. Is that possible? Or do I need some type of union?
  • Yuriy Faktorovich
    Yuriy Faktorovich almost 10 years
    @Mastro how about myBooks.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)));
  • Mastro
    Mastro almost 10 years
    Ok that code didn't help but it got my on my track to solving it. So gonna vote up your comment for the effort.
  • Yuriy Faktorovich
    Yuriy Faktorovich almost 10 years
    @Mastro did I misunderstand what you were asking? What was the solution?
  • Mastro
    Mastro almost 10 years
    Well I couldn't get yours to compile but I don't have books to chapters, I have projects(b) to companyperson(c) to companies(p). So I used linqPad and created a left join query. Too long to post here it seems, I'll post as an answer.
  • oɔɯǝɹ
    oɔɯǝɹ over 9 years
    While this code sample may answer the question, it lacks explanation. As it stands now, it adds no value, and stands the change of being downvoted / deleted. Please add some explanation what is does and why it is a solution for the problem of the OP.
  • Dejavu
    Dejavu about 7 years
    @Homer: I don't think your solution is possible, since b and c are inaccessible from the last Select clause. See live demo: rextester.com/BHNH3184
  • Homer
    Homer about 7 years
    @Dejavu, yeah my comment stinks. I'm not even sure what the point of it was.
  • mko
    mko about 6 years
    @YuriyFaktorovich but book.name is missing. this code only joins data from chapters and its childs
  • Pouya BCD
    Pouya BCD over 5 years
    Succinct! Thanks.