xsl-list
[Top] [All Lists]

Re: [xsl] Meunch away on this

2006-12-24 09:52:06
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(_at_)email(_dot_)com> 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


--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--



--~------------------------------------------------------------------
XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list
To unsubscribe, go to: http://lists.mulberrytech.com/xsl-list/
or e-mail: <mailto:xsl-list-unsubscribe(_at_)lists(_dot_)mulberrytech(_dot_)com>
--~--

<Prev in Thread] Current Thread [Next in Thread>