#8 Group books by authors in 3 ways
Grouping by Authors, a List inside of Books
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
-
Group
-
Hitchhikers Guide to the Galaxy by
Douglas Adams
-
Group
-
Good Omens by
Neil Gaiman,Terry Pratchett
-
Group
-
Phishing for Phools by
George Akerlof
-
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
-
Author: Douglas Adams
-
Hitchhikers Guide to the Galaxy by
Douglas Adams
-
Author: Neil Gaiman
-
Good Omens by
Neil Gaiman,Terry Pratchett
-
Author: Terry Pratchett
-
Good Omens by
Neil Gaiman,Terry Pratchett
-
The Last Continent by
Terry Pratchett
-
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
-
Douglas Adams
-
Hitchhikers Guide to the Galaxy
-
Terry Pratchett
-
Good Omens
co-authored by
Neil Gaiman
-
The Last Continent
-
Neil Gaiman
-
Good Omens
co-authored by
Terry Pratchett
-
George Akerlof
-
Raphael Müller (not an author)
-
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>
#8 Group books by authors in 3 ways
@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 })