Re: [xsl] Problem with grouping multi level nodes

Subject: Re: [xsl] Problem with grouping multi level nodes
From: "James A. Robinson" <jimr@xxxxxxxxxxxxxxxxxxxxx>
Date: Tue, 16 Mar 2004 22:29:49 -0800
>       <name>Student 1</name>    
>       <classes>            
>          <class>            
>             <title>soccer</title>        
>             <category>sport</category>                   
>          </class>            
>          <class>            
>             <title>football</title>        
>             <category>sport</category>                   
>          </class>            

So Student 1 has two class elements under classes, each of which
falls under category sport.

>    <p>Student group by Category</p>
>    <xsl:for-each select="/category[not(. = preceding::category)]">
>       <p><xsl:value-of select="."/></p>
> 
>       <xsl:for-each select="key('categories',.)">
>          <p><xsl:value-of select="../../name"/></p>
>       </xsl:for-each>
> 
>    </xsl:for-each>

The inner for-each you specify selects all categories matching the current
category and then outputs the name. That means when category is 'sport',
Student 1 matches twice.

To solve the problem you posted, I found the techniques discussed in
http://www.jenitennison.com/xslt/grouping/index.html to be useful.
If you use the Muenchian technique discussed under that URL, you can
select the first occurance of each unique title, category, and class
with a unique title-category combination, and then apply templates to
the student/name element for each match:

<?xml version='1.0' encoding='ISO-8859-1'?>
<xsl:stylesheet
  xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
  version='1.0'>

  <xsl:key name='title' match='title' use='.'/>
  <xsl:key name='category' match='category' use='.'/>
  <xsl:key name='class' match='class' use='concat(./title, " - ", ./category)'/>

  <xsl:key name='by_title' match='student' use='./classes/class/title'/>
  <xsl:key name='by_category' match='student' use='./classes/class/category'/>

  <xsl:output method='text' indent='no'/>

  <xsl:template match="students">
    <xsl:text>&#10;Student group by title&#10;&#10;</xsl:text>
    <xsl:apply-templates select='./student/classes/class/title
       [generate-id(.)=generate-id(key("title", .))]'>
      <xsl:sort select="."/>
    </xsl:apply-templates>

    <xsl:text>&#10;Student group by category&#10;&#10;</xsl:text>
    <xsl:apply-templates select='./student/classes/class/category
        [generate-id(.)=generate-id(key("category", .))]'>
      <xsl:sort select="."/>
    </xsl:apply-templates>

    <xsl:text>&#10;Student group by title and category&#10;&#10;</xsl:text>
    <xsl:apply-templates select='./student/classes/class
        [generate-id(.)=generate-id(key("class", concat(./title, " - ", ./category)))]'>
      <xsl:sort select="."/>
    </xsl:apply-templates>

  </xsl:template>

  <xsl:template match="title">
    <xsl:value-of select='.'/>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select='key("by_title", .)/name'>
      <xsl:sort select='.'/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="category">
    <xsl:value-of select='.'/>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select='key("by_category", .)/name'>
      <xsl:sort select='.'/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="class">
    <xsl:variable name='title_category' select='concat(./title, " - ", ./category)'/>
    <xsl:value-of select='$title_category'/>
    <xsl:text>&#10;</xsl:text>
    <xsl:apply-templates select='key("class", $title_category)/../../name'>
      <xsl:sort select='.'/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match='name'>
    <xsl:text>&#09;</xsl:text>
    <xsl:value-of select='.'/>
    <xsl:text>&#10;</xsl:text>
  </xsl:template>

</xsl:stylesheet>

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread