Trampoline Systems

* Trampoline Description Here

Trampoline Systems

* Trampoline Description Here


Content

Machines

Ideas, thoughts and observations from Trampoline's technical brains

mike

Select distinct with XSLT

By Mike Stenhouse on July 6th, 2007

While working on some tweaks to our website I decided that for the archive I needed to be able to select a list of categories used. Should be easy, right? Here’s a sample of the XML:

<machines-archive section="weblog" section-id="3">
<entry id="78" handle="installing-oracle-10g-on-64-bit-centos-5" linked-count="0">
<date year="2007" month="06" date="28" weekday="4">2007-06-28</date>
<time hour="12" minute="35">12:35</time>
<author />
<fields>
<title handle="installing-oracle-10g-on-64-bit-centos-5">installing Oracle 10g on 64 bit centos 5</title>
<categories>
<item handle="ruby">Ruby</item>
<item handle="java">Java</item>
<item handle="programming">Programming</item>
<item handle="code">Code</item>
</categories>
</fields>
<comments count="0" spam="0" />
</entry>
<entry id="75" handle="compulsory-wearing-of-facial-hair" linked-count="0">
<date year="2007" month="06" date="14" weekday="4">2007-06-14</date>
<time hour="14" minute="15">14:15</time>
<author />
<fields>
<title handle="compulsory-wearing-of-facial-hair">compulsory wearing of facial hair</title>
<categories>
<item handle="programming">Programming</item>
</categories>
</fields>
<comments count="0" spam="0" />
</entry>
</machines-archive>

I’m trying to get at machines-archive//item so this ought to would work, right?

<xsl:template match="machines-archive">
<xsl:for-each select=".//item[not(@handle = preceding::item/@handle)]">
<xsl:sort select="@handle" order="ascending"/>
<xsl:value-of select="@handle" /><xsl:text>, </xsl:text>
</xsl:for-each>
</xsl:template>

All the forum posts and mailing list archives I searched assured me that this’d be just fine… Except it wasn’t. It just didn’t work. Operating on all items in the document (//item) generated a distinct, de-duped set as expected, but as soon as I added the ‘.’ to work on a subset (.//item) it didn’t. Eh?!

Head-scratching, template-tweaking and tedious Googling ensued. I tried everything I could think of and after several hours of a lot of searching I eventually stumbled across a solution: EXSLT and set:distinct.

I first needed to add: xmlns:set="http://exslt.org/sets" to my stylesheet’s namespace. Then I could use set:distinct on my items:

<xsl:for-each select="set:distinct(.//item)">
<xsl:sort select="@handle" order="ascending"/>
<xsl:value-of select="@handle" /><xsl:text>, </xsl:text>
</xsl:for-each>

Job done. No one was pointing out this most elegant of solutions anywhere I found on the World Wide Web but it’s easy when you know how…

Leave a comment