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 &apos;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.