tags:

views:

82

answers:

1

Hi,

I know I can read a file into a variable like this: <xsl:variable name="content" select="document('file.xml')"/>, but how can I do the following:

file.xml:

<root>
  <child>Some</child>
  <child>Random</child>
  <child>Data</child>
  <child>Stuff</child>
</root>

Using that xml file, I'd like to print:

D
 * Data

R
 * Random

S
 * Stuff
 * Some

Meaning printing the caption only once and then listing the items alphabetically. I know I can use <xsl:for-each> and <xsl:sort>, but how to do that caption part?

Thanks for any help!

+2  A: 

There are several possible solutions, depending on what you want to do. The most well-known is the Muenchen method. This method first defines a lookup table to find all elements that share a particular key by that key. Then, you iterate over all keys (in some order) and for each key, over all key-values (in some order).

<xsl:key> can be used to make lookup tables. If you index of the first letter, you can then retrieve those elements which correspond to a particular letter.

<xsl:key name="child-by-first-letter" match="child" use="substring(.,1,1)"/>

Now, we can't iterate over all keys in a straightforward manner. However, we can iterate over all values and test whether they're the first value in the lookup table...

<xsl:for-each select="child[ generate-id() 
           = generate-id(key('child-by-first-letter', substring(.,1,1))[1])]"/>
    <xsl:sort select="substring(.,1,1)" /> <!--sort by first letter-->
    <xsl:value-of select="substring(.,1,1)">
    [...]

The above code will execute something only on child tags where the generate-id function returns the same value as the generate-id function does for the first element of the list of children with the same first letter. Since generate-id generates unique id's, this will only return only those children that are "first" in the lookup table for their particular key.

Now, you're almost done: just iterate over all children with that particular first letter:

    <ul>
         <xsl:for-each select="key('child-by-first-letter', substring(.,1,1))">
             <!--no sort, we'll do document order-->
             <li><xsl:value-of select="."/></li>
         </xsl:for-each>
    </ul>
</xsl:for-each>

This is called the Muenchian method.

Eamon Nerbonne
And if xslt 2.0 processor is available then by all means use the built in for-each-group command (http://www.zvon.org/xxl/XSL-Ref/Tutorials/For-Each-Group/feg9.html)
Goran
Oh yes, if XSLT 2.0 is available, jump on that the first chance you get :-) - it's much more intuitive in this and many other things.
Eamon Nerbonne