[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: Meunch away on this

Subject: Re: Meunch away on this
From: "Andrew Welch" <andrew.j.welch@xxxxxxxxx>
Date: Sun, 24 Dec 2006 16:51:33 +0000
xsl sort identity template
This variation of the identity template sorts by element name and
removes duplicates:

<xsl:template match="*">
 <xsl:if test="not(preceding::*[name() = name(current())])">
   <xsl:copy>
     <xsl:apply-templates select="*">
       <xsl:sort select="name()"/>
     </xsl:apply-templates>
   </xsl:copy>
 </xsl:if>
</xsl:template>

cheers
andrew

On 12/22/06, Thomas Stone <stonethomasjames@xxxxxxxxx> wrote:
I am looking for feedback on my solution to a very old topic... sorting and grouping in XSLT version 1.0. I am using Mozilla Firefox version 1.0.7 to read an XML document referencing an XSLT stylesheet to produce a simple HTML table. The desired data is, oddly enough, the element names of the XML document.

Any source XML document will do. It will need to have a processor directive pointing to the below sample stylesheet.

<?xml-stylesheet type="text/xsl" href="Display_Entities.xml"?>


To list all the elements of that document, the stylesheet would be as follows:


<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:template match="/">
   <html><head><title>Tags List</title></head>
     <body><table border="1">
         <tr><th>Tag Name</th></tr>

         <xsl:apply-templates select="//*" mode="all">
           <xsl:sort select="name()"/>
         </xsl:apply-templates>
     </table></body>
   </html>
 </xsl:template>


<xsl:template match="*" mode="all"> <tr> <td><xsl:value-of select="name()"/></td> </tr> </xsl:template> </xsl:stylesheet>


This list is sorted and shows all the data I need, but the question that I've seen posts on back to 1999 is how to make this a sorted unique list. My hat's still off to Steve Meunch for the key value solution. I don't even want to try to figure out a faster way to uniquely sort a list. I was only interested in finding a less, if you'll pardon me, convoluted way to do it so it could be implemented without a long explanation. Though, again, thanks to Jeni for her site.


Here is what I came up with that seems pretty straight forward. Using the Position() function within the sorted list, only output the first position. This will always get the first entry in alphabetical order. Select sorted all elements that are not the same name as the first and recurse to the same procedure, thus displaying only the second entry in alphabetical order. Append each entry to a delimited string array and use a Contains() test to eliminate duplicates from the next selection list.

         <xsl:apply-templates select="//*" mode="unique">
           <xsl:sort select="name()"/>
           <xsl:with-param name="code_list" select="';'"/>
         </xsl:apply-templates>


<xsl:template match="*" mode="unique"> <xsl:param name="code_list"/>

<xsl:if test="position()=1">

<xsl:variable name="ent_name" select="name()"/>

     <tr>
       <td><xsl:value-of select="$ent_name"/></td>
     </tr>

<xsl:variable name="new_list" select="concat($code_list, concat($ent_name, ';'))"/>

     <xsl:apply-templates select="//*[contains($new_list, concat(';', concat(name(), ';')))=false()]" mode="unique">
       <xsl:sort select="name()"/>
       <xsl:with-param name="code_list" select="$new_list"/>
     </xsl:apply-templates>
   </xsl:if>
 </xsl:template>



This gives me a unique sorted list of all entities in the document. I prefer to have them enumerated.

         <xsl:apply-templates select="//*" mode="summary">
           <xsl:sort select="name()"/>
           <xsl:with-param name="code_list" select="';'"/>
           <xsl:with-param name="seq_counter" select="1"/>
         </xsl:apply-templates>


<xsl:template match="*" mode="summary"> <xsl:param name="code_list"/> <xsl:param name="seq_counter"/>

<xsl:if test="position()=1">

<xsl:variable name="ent_name" select="name()"/>

     <tr>
       <td><xsl:value-of select="$seq_counter"/></td>
         <td><xsl:value-of select="$ent_name"/></td>
     </tr>

<xsl:variable name="new_list" select="concat($code_list, concat($ent_name, ';'))"/>

     <xsl:apply-templates select="//*[contains($new_list, concat(';', concat(name(), ';')))=false()]" mode="summary">
       <xsl:sort select="name()"/>
       <xsl:with-param name="code_list" select="$new_list"/>
       <xsl:with-param name="seq_counter" select="$seq_counter+1"/>
     </xsl:apply-templates>
   </xsl:if>
 </xsl:template>



From this structure, I can group by placing a correlated sub-query <apply-templates> where the <tr> output is. The uniqueness test can be applied just as directly to character data or attributes.


-- ___________________________________________________ Search for products and services at: http://search.mail.com

Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.