aminedev
aminedev

Reputation: 323

xsl , xml group by

Hey i have the foolowing xml file :

<?xml version="1.0"?>
 <accidents>
 <accident>
   <org>1</org>
   <com>194</com>
   <dep>010</dep>
   <grav>0.64</grav>
 </accident>
 <accident>
   <org>1</org>
   <com>194</com>
   <dep>420</dep>
   <grav>0.54</grav>
 </accident>
 <accident>
   <org>1</org>
   <com>44</com>
   <dep>010</dep>
   <grav>0.4</grav>
</accident>
</accidents>

And i want to apply an xslt 1.0 to have the number of accidents by dep : the output should be as an html page like this :

 <table> <thead> <tr> <th>420</th> <th>010</th> </tr> </thead> <tbody> <tr> 
   <th>accidents</th> <td>1</td> <td>2</td> </tr> </tbody> </table>

thanks ,note i use php5

Upvotes: 0

Views: 179

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243569

You can use almost the same answer as that to your previous question:

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

     <xsl:key name="kAccByDept" match="accident" use="dep"/>

     <xsl:template match="/*">
      <html>
       <table border="1">
         <thead>
           <tr>
            <th>deps:</th>
             <xsl:apply-templates/>
           </tr>
         </thead>
         <tbody>
            <tr>
             <th>accidents:</th>
             <xsl:apply-templates mode="count"/>
           </tr>
         </tbody>
       </table>
      </html>
     </xsl:template>

     <xsl:template match=
      "accident
        [generate-id()
        =
         generate-id(key('kAccByDept', dep)[1])
         ]">
         <td><xsl:value-of select="dep"/></td>
     </xsl:template>
     <xsl:template mode="count" match=
      "accident
        [generate-id()
        =
         generate-id(key('kAccByDept', dep)[1])
         ]">
         <td><xsl:value-of select="count(key('kAccByDept', dep))"/></td>
     </xsl:template>
     <xsl:template match="text()"/>
     <xsl:template match="text()" mode="count"/>
</xsl:stylesheet>

When this transformation is applied to the provided XML document:

<accidents>
    <accident>
        <org>1</org>
        <com>194</com>
        <dep>010</dep>
        <grav>0.64</grav>
    </accident>
    <accident>
        <org>1</org>
        <com>194</com>
        <dep>420</dep>
        <grav>0.54</grav>
    </accident>
    <accident>
        <org>1</org>
        <com>44</com>
        <dep>010</dep>
        <grav>0.4</grav>
    </accident>
</accidents>

the correct result is produced (with added "deps:" in the heading to achieve proper alignment):

<html>
   <table border="1">
      <thead>
         <tr>
            <th>deps:</th>
            <td>010</td>
            <td>420</td>
         </tr>
      </thead>
      <tbody>
         <tr>
            <th>accidents:</th>
            <td>2</td>
            <td>1</td>
         </tr>
      </tbody>
   </table>
</html>

Things become more interesting if there are other events than accidents and departments without accidents. Suppose we now have this XML document:

<events>
    <accidents>
        <accident>
            <org>1</org>
            <com>194</com>
            <dep>010</dep>
            <grav>0.64</grav>
        </accident>
        <accident>
            <org>1</org>
            <com>194</com>
            <dep>420</dep>
            <grav>0.54</grav>
        </accident>
        <accident>
            <org>1</org>
            <com>44</com>
            <dep>010</dep>
            <grav>0.4</grav>
        </accident>
    </accidents>
    <achievements>
        <achievement>
            <org>1</org>
            <com>194</com>
            <dep>002</dep>
            <grav>0.64</grav>
        </achievement>
        <achievement>
            <org>1</org>
            <com>194</com>
            <dep>420</dep>
            <grav>0.54</grav>
        </achievement>
        <achievement>
            <org>1</org>
            <com>44</com>
            <dep>011</dep>
            <grav>0.4</grav>
        </achievement>
    </achievements>
</events>

Here is again a transformation which correctly produces all different departments and their number of accidents:

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

     <xsl:key name="kAccByDept" match="accident" use="dep"/>
     <xsl:key name="kDepByCode" match="dep" use="."/>

     <xsl:variable name="vDeps" select=
     "/*/*/*/dep
             [generate-id()
             =
              generate-id(key('kDepByCode', .)[1])
             ]
     "/>

     <xsl:template match="/*">
      <html>
       <table border="1">
         <thead>
           <tr>
            <th>deps:</th>
             <xsl:apply-templates select="$vDeps"/>
           </tr>
         </thead>
         <tbody>
            <tr>
             <th>accidents:</th>
             <xsl:apply-templates select="$vDeps" mode="acc"/>
           </tr>
         </tbody>
       </table>
      </html>
     </xsl:template>

     <xsl:template match="dep">
         <td><xsl:value-of select="."/></td>
     </xsl:template>

     <xsl:template match="dep" mode="acc">
         <td><xsl:value-of select="count(key('kAccByDept', .))"/></td>
     </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the above XML document, it produces the correct, wanted result:

<html>
   <table border="1">
      <thead>
         <tr>
            <th>deps:</th>
            <td>010</td>
            <td>420</td>
            <td>002</td>
            <td>011</td>
         </tr>
      </thead>
      <tbody>
         <tr>
            <th>accidents:</th>
            <td>2</td>
            <td>1</td>
            <td>0</td>
            <td>0</td>
         </tr>
      </tbody>
   </table>
</html>

UPDATE: In a comment the OP asked also to have the sum of "com" per department.

The transformation needs to changed only slightly:

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

         <xsl:key name="kAccByDept" match="accident" use="dep"/>
         <xsl:key name="kDepByCode" match="dep" use="."/>

         <xsl:variable name="vDeps" select=
         "/*/*/*/dep
                 [generate-id()
                 =
                  generate-id(key('kDepByCode', .)[1])
                 ]
         "/>

         <xsl:template match="/*">
          <html>
           <table border="1">
             <thead>
               <tr>
                <th>deps:</th>
                 <xsl:apply-templates select="$vDeps"/>
               </tr>
             </thead>
             <tbody>
                <tr>
                 <th>accidents:</th>
                 <xsl:apply-templates select="$vDeps" mode="acc"/>
               </tr>
                <tr>
                 <th>coms:</th>
                 <xsl:apply-templates select="$vDeps" mode="com"/>
               </tr>
             </tbody>
           </table>
          </html>
         </xsl:template>

         <xsl:template match="dep">
             <td><xsl:value-of select="."/></td>
         </xsl:template>

         <xsl:template match="dep" mode="acc">
             <td><xsl:value-of select="count(key('kAccByDept', .))"/></td>
         </xsl:template>
         <xsl:template match="dep" mode="com">
             <td><xsl:value-of select="sum(key('kAccByDept', .)/com)"/></td>
         </xsl:template>
</xsl:stylesheet>

Now, when applied on the same XML document (above), again the wanted answer is produced:

<html>
   <table border="1">
      <thead>
         <tr>
            <th>deps:</th>
            <td>010</td>
            <td>420</td>
            <td>002</td>
            <td>011</td>
         </tr>
      </thead>
      <tbody>
         <tr>
            <th>accidents:</th>
            <td>2</td>
            <td>1</td>
            <td>0</td>
            <td>0</td>
         </tr>
         <tr>
            <th>coms:</th>
            <td>238</td>
            <td>194</td>
            <td>0</td>
            <td>0</td>
         </tr>
      </tbody>
   </table>
</html>

Upvotes: 1

Kirill Polishchuk
Kirill Polishchuk

Reputation: 56202

Use:

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

  <xsl:key name="k" match="accident" use="dep"/>

  <xsl:template match="/accidents">
    <xsl:variable name="accidents" select="accident[generate-id() 
                  = generate-id(key('k', dep))]"/>

    <table>
      <thead>
        <tr>
          <xsl:for-each select="$accidents">
            <th>
              <xsl:value-of select="dep"/>
            </th>
          </xsl:for-each>
        </tr>
      </thead>
      <tbody>
        <tr>
          <xsl:for-each select="$accidents">
            <td>
              <xsl:value-of select="count(key('k', dep))"/>
            </td>
          </xsl:for-each>
        </tr>
      </tbody>
    </table>
  </xsl:template>

</xsl:stylesheet>

Upvotes: 0

Related Questions