1

I have a XML file as following and I want to Group the books based on the published year and count the number of books for each year. sort the result by ascending order.

<body>
  <outline text="A">
    <outline text="Abelson, Harold" author="Harold Abelson" title="Struktur und Interpretation von Computerprogrammen. Eine Informatik-Einführung" publisher="Springer Verlag" isbn="3540520430" year="1991"/>
    <outline text="Abrahams, Paul W." author="Paul W. Abrahams" title="Tex for the Impatient" publisher="Addison-Wesley Pub Co" isbn="0201513757" year="2000"/>
  </outline>
  <outline text="B">
    <outline text="Bach, Fred" author="Fred Bach" title="UNIX Handbuch zur Programmentwicklung" publisher="Hanser Fachbuchverlag" isbn="3446151036"/>
    <outline text="Bach, Maurice J." author="Maurice J. Bach" title="Design of the UNIX Operating System" publisher="Prentice Hall PTR" isbn="0132017997" year="1986"/>
  </outline>
</body>

Here is the excepted output

    <p> Group by Year </p>
    <table border="1">
        <tr bgcolor="yellow">
            <th>Year</th>
            <th>Number</th>
        </tr>
        <tr>
            <td>1963</td>
            <td>1</td>
        </tr>
        <tr>
            <td>1966</td>
            <td>1</td>
        </tr>

Here is my code:

<xsl:template match="body">
    <xsl:copy>
      <table border="1">     
        <tr bgcolor="Yellow">
            <th class="cell"><span> Year </span></th>
            <th class="cell"><span> Count </span></th>
        </tr>
        <xsl:apply-templates select="outline"/>
      </table>
    </xsl:copy>
  </xsl:template>

 <xsl:key  name="groupbyyear"  match="outline" use="@year"/)/>
   <xsl:template match="outline/outline">
    <tr>
      <xsl:sort select="year" order="ascending" />
    </tr>
  </xsl:template>


</xsl:stylesheet>

I'm not sure how to use the xsl key function and what should I do inside the template after the key function?

1 Answer 1

1

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kOutlineByYear" match="outline/outline" use="@year"/>

 <xsl:template match="/*">
    <p> Group by Year </p>
    <table border="1">
        <tr bgcolor="yellow">
            <th>Year</th>
            <th>Number</th>
        </tr>
     <xsl:apply-templates select=
      "outline/outline[generate-id()
                      =generate-id(key('kOutlineByYear',@year)[1])
                      ]">
      <xsl:sort select="@year" data-type="number"/>
     </xsl:apply-templates>
    </table>
 </xsl:template>

 <xsl:template match="outline/outline">
   <tr>
     <td><xsl:value-of select="@year"/></td>
     <td><xsl:value-of select="count(key('kOutlineByYear',@year))"/></td>
   </tr>
 </xsl:template>
</xsl:stylesheet>

when applied on the following XML document (the provided one with small corrections to make it more representative):

<body>
  <outline text="A">
    <outline text="Abelson, Harold"
     author="Harold Abelson"
     title="Struktur und Interpretation von Computerprogrammen. Eine Informatik-Einführung"
     publisher="Springer Verlag"
     isbn="3540520430" year="1991"/>
    <outline text="Abrahams, Paul W." author="Paul W. Abrahams"
     title="Tex for the Impatient"
     publisher="Addison-Wesley Pub Co" isbn="0201513757" year="2000"/>
  </outline>
  <outline text="B">
    <outline text="Bach, Fred" author="Fred Bach"
    title="UNIX Handbuch zur Programmentwicklung"
    publisher="Hanser Fachbuchverlag" isbn="3446151036" year="2000"/>
    <outline text="Bach, Maurice J."
    author="Maurice J. Bach"
    title="Design of the UNIX Operating System"
    publisher="Prentice Hall PTR" isbn="0132017997" year="1986"/>
  </outline>
</body>

produces the wanted, correct result:

<p> Group by Year </p>
<table border="1">
   <tr bgcolor="yellow">
      <th>Year</th>
      <th>Number</th>
   </tr>
   <tr>
      <td>1986</td>
      <td>1</td>
   </tr>
   <tr>
      <td>1991</td>
      <td>1</td>
   </tr>
   <tr>
      <td>2000</td>
      <td>2</td>
   </tr>
</table>

Explanation:

Proper use of the Muenchian grouping method.


II. XSLT 2.0 solution:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:key name="kOutlineByYear" match="outline/outline" use="@year"/>

 <xsl:template match="/*">
    <p> Group by Year </p>
    <table border="1">
        <tr bgcolor="yellow">
            <th>Year</th>
            <th>Number</th>
        </tr>
     <xsl:for-each-group select="outline/outline"
      group-by="@year">
       <xsl:sort select="current-grouping-key()" data-type="number"/>
           <tr>
             <td><xsl:value-of select="current-grouping-key()"/></td>
             <td><xsl:value-of select="count(current-group())"/></td>
           </tr>
      </xsl:for-each-group>
    </table>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the same XML document (above), again the same correct result is produced:

<p> Group by Year </p>
<table border="1">
   <tr bgcolor="yellow">
      <th>Year</th>
      <th>Number</th>
   </tr>
   <tr>
      <td>1986</td>
      <td>1</td>
   </tr>
   <tr>
      <td>1991</td>
      <td>1</td>
   </tr>
   <tr>
      <td>2000</td>
      <td>2</td>
   </tr>
</table>

Explanation:

  1. Use of the XSLT 2.0 instruction <xsl:for-each-group> with the group-by attribute.

  2. Use of the XSLT 2.0 functions current-group() and current-grouping-key().

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you very much, I tried to sort the table by year <xsl:template match="outline/outline"> <xsl:sort select="@year" order="ascending"/> why this is not working?
@Michael, You are welcome. In the <xsl:sort> you have forgotten to specify the attribute: data-type="number". I corrected the two transformations to do the sorting.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.