Skip to main content
Home  › ... Razor

LINQ Tutorials

Tutorial HomeLINQ

Initial Code

The following code runs at the beginning and creates some variables/services used in the following samples.

@{
  var persons = AsList(App.Data["Persons"]);
  var books = AsList(App.Data["Books"]);
}

GroupBy Authors - bad example ⚠️

This example will group the books by the Authors property. This example doesn't give us what we want, because authors that exist in sets and alone are grouped separately.

Output

  1. Group
    • Hitchhikers Guide to the Galaxy by Douglas Adams
  2. Group
    • Good Omens by Neil Gaiman,Terry Pratchett
  3. Group
    • Phishing for Phools by George Akerlof
  4. Group
    • The Last Continent by Terry Pratchett
<ol>
  @foreach (var group in books.GroupBy(b => b.Authors)) {
    <li>
      Group
      <ul>
        @foreach (var book in group) {
          <li>
            <strong>@book.Title</strong> by
            @string.Join(",", AsList(book.Authors as object).Select(a => a.FirstName + " " + a.LastName))
          </li>
        }
      </ul>
    </li>
  }
</ol>

GroupBy Authors - better example

Let's do it better, and group by each Author individually

Output

  1. Author: Douglas Adams
    • Hitchhikers Guide to the Galaxy by Douglas Adams
  2. Author: Neil Gaiman
    • Good Omens by Neil Gaiman,Terry Pratchett
  3. Author: Terry Pratchett
    • Good Omens by Neil Gaiman,Terry Pratchett
    • The Last Continent by Terry Pratchett
  4. Author: George Akerlof
    • Phishing for Phools by George Akerlof
<ol>
  @{ 
    var booksGroupedByAuthor = books
      .SelectMany(b => AsList(b.Authors as object).Select(a => new { Book = b, Author = a }))
      .GroupBy(set => set.Author);
      
    foreach (var group in booksGroupedByAuthor) {
    <li>
      Author: @group.Key.FirstName @group.Key.LastName
      <ul>
        @foreach (var set in group) {
          <li>
            <strong>@set.Book.Title</strong> by
            @string.Join(",", AsList(set.Book.Authors as object).Select(a => a.FirstName + " " + a.LastName))
          </li>
        }
      </ul>
    </li>
  }
  }
</ol>

GroupBy Authors - Example starting with Authors

In this example, we'll start with the authors list. This is probably not ideal - as some people are not authors, but it's a good learning example. To find the books we have to navigate through Parents(...) because in our data-model, the books reference authors, not the authors to books.

Output

  1. Douglas Adams
    • Hitchhikers Guide to the Galaxy
  2. Terry Pratchett
    • Good Omens co-authored by Neil Gaiman
    • The Last Continent
  3. Neil Gaiman
    • Good Omens co-authored by Terry Pratchett
  4. George Akerlof
    • Phishing for Phools
  5. Raphael Müller (not an author)
  6. Ed Hardy
<ol>
  @foreach (var author in persons) {
    // this line would work, if Books only had people in the Authors. 
    // but it doesn't, there are also illustrators, which is why we use the second example instead
    var peoplesBooks = author.Parents("Books");
    var authorsBooks = author.Parents("Books", "Authors");

    <li>
      @author.FirstName @author.LastName
      <ul>
        @foreach (var book in authorsBooks) {
          var coAuthors = AsList(book.Authors as object).Where(a => a != author);
          <li>
            <strong>@book.Title</strong> 
            @if (coAuthors.Any()) {
              <span>co-authored by 
                @string.Join(",", coAuthors.Select(a => a.FirstName + " " + a.LastName))
              </span>
            }
          </li>
        }
      </ul>
    </li>
  }
</ol>

Source Code of this file

Below you'll see the source code of the file. Note that we're just showing the main part, and hiding some parts of the file which are not relevant for understanding the essentials. Click to expand the code

@inherits Custom.Hybrid.Razor14
@using ToSic.Razor.Blade;
@using System.Linq;
<!-- unimportant stuff, hidden -->


<div @Sys.PageParts.InfoWrapper()>
  @Html.Partial("../shared/DefaultInfoSection.cshtml")
  <div @Sys.PageParts.InfoIntro()>
    <h2>Grouping by Authors, a List inside of Books</h2>
  </div>
</div>

  @{
    var persons = AsList(App.Data["Persons"]);
    var books = AsList(App.Data["Books"]);
  }



<h3>GroupBy Authors - <em>bad example</em> ⚠️</h3>
<p>This example will group the books by the <code>Authors</code> property. This example doesn't give us what we want, because authors that exist in sets and alone are grouped separately. </p>

  <ol>
    @foreach (var group in books.GroupBy(b => b.Authors)) {
      <li>
        Group
        <ul>
          @foreach (var book in group) {
            <li>
              <strong>@book.Title</strong> by
              @string.Join(",", AsList(book.Authors as object).Select(a => a.FirstName + " " + a.LastName))
            </li>
          }
        </ul>
      </li>
    }
  </ol>



<h3>GroupBy Authors - better example</h3>
<p>Let's do it better, and group by each Author individually</p>

  <ol>
    @{ 
      var booksGroupedByAuthor = books
        .SelectMany(b => AsList(b.Authors as object).Select(a => new { Book = b, Author = a }))
        .GroupBy(set => set.Author);
      
      foreach (var group in booksGroupedByAuthor) {
      <li>
        Author: @group.Key.FirstName @group.Key.LastName
        <ul>
          @foreach (var set in group) {
            <li>
              <strong>@set.Book.Title</strong> by
              @string.Join(",", AsList(set.Book.Authors as object).Select(a => a.FirstName + " " + a.LastName))
            </li>
          }
        </ul>
      </li>
    }
    }
  </ol>



<h3>GroupBy Authors - Example starting with Authors</h3>
<p>In this example, we'll start with the authors list. This is probably not ideal - as some people are not authors, but it's a good learning example. To find the books we have to navigate through <code>Parents(...)</code> because in our data-model, the books reference authors, not the authors to books.</p>

  <ol>
    @foreach (var author in persons) {
      // this line would work, if Books only had people in the Authors. 
      // but it doesn't, there are also illustrators, which is why we use the second example instead
      var peoplesBooks = author.Parents("Books");
      var authorsBooks = author.Parents("Books", "Authors");

      <li>
        @author.FirstName @author.LastName
        <ul>
          @foreach (var book in authorsBooks) {
            var coAuthors = AsList(book.Authors as object).Where(a => a != author);
            <li>
              <strong>@book.Title</strong> 
              @if (coAuthors.Any()) {
                <span>co-authored by 
                  @string.Join(",", coAuthors.Select(a => a.FirstName + " " + a.LastName))
                </span>
              }
            </li>
          }
        </ul>
      </li>
    }
  </ol>



@* Footer *@
@Html.Partial("../Shared/Layout/FooterWithSource.cshtml", new { Sys = Sys })