Muenchian Method grouping in XSLT
I owe a deal of thanks for figuring out the Muenchian Method of grouping in XSLT (1.0) to Jeni's article Grouping Using the Muenchian Method. It took, however, a while for me to get my mind around the method completely, and some experimentation, which I'm sharing below.
Specifically I was looking to get a listing of tracks, from my iTunes Playlists to Xml application's output, and group them by album.
For this, all I really need is the track's album and name, which gives a basic layout of the following.
<playlist>
<track><name>Smile</name><album>Alright, Still</album></track>
<track><name>Knock 'Em Out</name><album>Alright, Still</album></track>
<!-- Additional tracks removed. -->
</playlist>
Now the first thing you'll need is a key, that you'll be using to group things by. In this case, since I want to group by album, that will be my key. Immediately before any <xsl:template/> elements I added the following key.
<xsl:key name="tracks-by-album"
match="track"
use="album"/>
Here we're grouping the tracks, by the album value.
Now that we have a key, we can, for example, grab just those tracks that are from a certain album.
<xsl:template match="/">
<ul>
<xsl:for-each select="key('tracks-by-album', '1492: Conquest of Paradise')">
<li><xsl:value-of select="name"></xsl:value-of></li>
</xsl:for-each>
</ul>
</xsl:template>
This will only output those tracks from the album Vangelis' 1492: Conquest of Paradise.
Stepping back, if I just wanted to grab the first track's album, I could do the following.
<xsl:template match="/">
<ul>
<xsl:for-each select="/playlist/track[1]">
<li><xsl:value-of select="album"/></li>
</xsl:for-each>
</ul>
</xsl:template>
That suggests that this code is doing, which will go through every album, and display the first track.
<xsl:template match="/">
<ul>
<xsl:for-each select="/playlist/track[count(. | key('tracks-by-album', album)[1]) = 1]">
<xsl:sort select="album"/>
<li><xsl:value-of select="album"/>
<ul>
<li><xsl:value-of select="name"/></li>
</ul>
</li>
</xsl:for-each>
</ul>
</xsl:template>
And with a slight tweak, you can loop through and display all the song names from each album.
<xsl:template match="/">
<ul>
<xsl:for-each select="/playlist/track[count(. | key('tracks-by-album', album)[1]) = 1]">
<xsl:sort select="album"/>
<li>
<xsl:value-of select="album"/>
<ul>
<xsl:for-each select="key('tracks-by-album', album)">
<li><xsl:value-of select="name"/></li>
</xsl:for-each>
</ul>
</li>
</xsl:for-each>
</ul>
</xsl:template>
And after all that discovery, I have exactly what I want; a listing of tracks, grouped by albums. With only slight modification, the grouping can be changed to artist, or any one of any other number of items.
Now if only IE 7+ supported for-each-group, available in XSLT 2.0, this wouldn't be necessary.
Search
Links of Note
Support This Site
If my blog was helpful to you, then please consider visiting my Amazon Wishlist.