Skip to content

Cory Foy

Organizational agility through intersecting business and technology

Menu
  • FASTER Fridays
  • Mapping Mondays
  • Player Embed
  • Search Videos
  • User Dashboard
  • User Videos
  • Video Category
  • Video Form
  • Video Tag
Menu

Getting a distinct list of values in XSL from a substring of data in a node

Posted on September 12, 2007June 11, 2010 by Cory Foy

Today’s post comes from a tutorial I’m working on for a customer who still does a lot of XML/XSL stuff. One of the more challenging things to do in XSL1.0 (which is what most browsers support) was getting a distinct list of values from a set of nodes. Exslt has a good article on the subject, including this link to Jeni Tenison’s XSLT Template. In the back of my mind, I kept wondering if there was some way to do this with keys, and the customer I’m working with showed a way they do it.

Let’s say you have an XML document like:

<addresses>
  </addresses></font><address>
    <state>FL</state>
  </address>
  <address>
    <state>GA</state>
  </address>
  <address>
    <state>MN</state>
  </address>
  <address>
    <state>FL</state>
  </address>

And you want to output something like:

<states>
  <state>FL</state>
  <state>GA</state>
  <state>MN</state>
</states>

The way to do this with keys is to define an xsl:key like this:

<xsl:key name=”distinctState” match=”Addresses/Address” use=”./State”></xsl:key>

This let’s us set up a key to access Address nodes with. We can then get the distinct list in an xsl:for-each node by combining this with the generate-key function:

<xsl:for-each select=”Addresses/Address[generate-id() = generate-id(key(‘distinctState’, ./State))”></xsl:for-each>

generate-id “generates a key that uniquely identifies a specified node“, in this case, basically creating a node-set of the state nodes and returning the first node for each distinct value. So our full xsl would look something like:

<xsl:key name=”distinctState” match=”Addresses/Address” use=”./State”>
<xsl:template match=”/”>
<states>
<font face=”Courier New”><xsl:for-each select=”Addresses/Address[generate-id() = generate-id(key(‘distinctState’, ./State))”></xsl:for-each></font>
  <state><xsl:value-of select=”.”></xsl:value-of></state>

</states></xsl:template></xsl:key>

Which is all well and good. However, in the tutorial example, state isn’t in its own node – it’s embedded in the Address like:

<address>123 Sample Way, Tampa, FL</address>

Which is a tad trickier. In the tutorial, we’ve allowed the assumption that the state will always be the last 2 characters of the Address field. So how can we get a distinct list of states with data like this?

Turns out that we can do it in a very similar (if complex) way. We start off by specifying the key:

<xsl:key name=”distinctState” match=”/Customers/Customer” use=”substring(Address, string-length(Address)-1)”></xsl:key>

Here, our Address node is a child of Customer, which is a child of Customers – the root node. So we are matching Customers/Customer, and using the value of the last 2 characters of the Address. We then need to do the same for our for-each loop:

<xsl:for-each select=”Customers/Customer[generate-id() = generate-id(key(‘distinctState’, substring(Address, (string-length(Address)-1))))]”>
  <xsl:call-template name=”AggregateForState”>
    <xsl:with-param>
       </xsl:with-param></xsl:call-template></xsl:for-each>
name=”state”
       select=”substring(Address, (string-length(Address)-1))”/>
  

So we are doing the unique select on the state value. However, this returns the matching node – which is an Address node, so to use the value (in our example here as a parameter to a named template) we have to still substring it out.

Of course, this would probably be a better time to either see if you can get State into its own node, or do some sort of pre-processing to do that, but when you have neither of those options, this will work.

Thanks to Len and the SR team for the initial key idea

7 thoughts on “Getting a distinct list of values in XSL from a substring of data in a node”

  1. Anonymous says:
    September 14, 2007 at 2:48 pm

    Or you could just put a bullet in your head.

  2. Nicky says:
    December 5, 2007 at 2:22 am

    pls put ] at the end of xsl:for-each
    before closing xsl:for-each
    Else everything workig fine.
    Thanks a lot.
    Karishma

  3. Anonymous says:
    November 20, 2008 at 4:45 am

    A whole year after this post, and the information is very helpful. Thanks very much.

    Carl.

  4. Anonymous says:
    May 13, 2009 at 9:57 pm

    two years, still helpful

  5. Joe Schmoe says:
    September 23, 2009 at 2:39 pm

    I agree with the first commenter. Seems like that would be much quicker and less painful.

  6. San says:
    November 6, 2009 at 7:55 am

    I am looking for some thing on this lines, but the code lines are missing, can you please update it.

    Cheers,
    San

  7. Kevin says:
    June 10, 2010 at 3:06 am

    Please update the code lines! Without, the tut is not really a tut.

    Thanks,
    Kevin

Comments are closed.

© 2025 Cory Foy | Powered by Superbs Personal Blog theme