Reputation: 15
I have a complex XML file structured by book title. Something like this, but with hundreds of books and sometimes many authors per book.
<Book>
<Title>Ken Lum</Title>
<Author>
<GivenName>Grant</GivenName>
<Surname>Arnold</Surname>
</Author>
</Book>
<Book>
<Title>Shore, Forest and Beyond</Title>
<Author>
<GivenName>Ian M.</GivenName>
<Surname>Thom</Surname>
</Author>
<Author>
<GivenName>Grant</GivenName>
<Surname>Arnold</Surname>
</Author>
</Book>
What I need to output is an alphabetized list of authors, and then a list of every book they worked on, also alphabetized, something like:
Arnold, Grant — Ken Lum; Shore, Forest and Beyond
Thom, Ian M. — Shore, Forest and Beyond
I have a version of the code working fairly well, but it is very slow, so I'm trying to optimize my approach. I recently learned of the Muenchian method of grouping from another user here and I'm trying to apply that.
The part I'm specifically stuck on right now is getting the list of titles per author. This is what I have right now:
<xsl:key name="books-by-author" match="Book"
use="concat(Author/GivenName, Contributor/Surname)" />
…
<xsl:template match="Author">
…
<xsl:apply-templates mode="ByAuthor" select=
"key('books-by-author',
concat(GivenName, Surname)
)">
<xsl:sort select="Title/TitleText"/>
</xsl:apply-templates>
</template>
But it seems that this is only matching Books where the Author is the first one listed, like:
Arnold, Grant — Ken Lum
Thom, Ian M. — Shore, Forest and Beyond
I figure the xsl:key
is only using the first Author
element, rather than checking every author. Is it possible to check every Author
like that? Or is there a better approach?
Upvotes: 0
Views: 254
Reputation: 117165
I would suggest you look at this way:
XML
<Books>
<Book>
<Title>Ken Lum</Title>
<Author>
<GivenName>Grant</GivenName>
<Surname>Arnold</Surname>
</Author>
</Book>
<Book>
<Title>Shore, Forest and Beyond</Title>
<Author>
<GivenName>Ian M.</GivenName>
<Surname>Thom</Surname>
</Author>
<Author>
<GivenName>Grant</GivenName>
<Surname>Arnold</Surname>
</Author>
</Book>
</Books>
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="author" match="Author" use="concat(Surname, ', ', GivenName)" />
<xsl:template match="/Books">
<Authors>
<!-- for each unique author -->
<xsl:for-each select="Book/Author[count(. | key('author', concat(Surname, ', ', GivenName))[1]) = 1]">
<xsl:sort select="Surname"/>
<xsl:sort select="GivenName"/>
<Author>
<!-- author's details-->
<xsl:copy-of select="Surname | GivenName"/>
<!-- list author's books -->
<Books>
<xsl:for-each select="key('author', concat(Surname, ', ', GivenName))/parent::Book">
<xsl:sort select="Title"/>
<xsl:copy-of select="Title"/>
</xsl:for-each>
</Books>
</Author>
</xsl:for-each>
</Authors>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<Authors>
<Author>
<GivenName>Grant</GivenName>
<Surname>Arnold</Surname>
<Books>
<Title>Ken Lum</Title>
<Title>Shore, Forest and Beyond</Title>
</Books>
</Author>
<Author>
<GivenName>Ian M.</GivenName>
<Surname>Thom</Surname>
<Books>
<Title>Shore, Forest and Beyond</Title>
</Books>
</Author>
</Authors>
Upvotes: 1