Search code examples
xsltxslt-1.0exslt

XSLT key not available inside template (using note-set array)


I created an XLT document with an array of values for each of the hour of the day. I loop through this array by creating a variable using node-set.

I have a key created that holds the total counts for each hour called key-hours. However, when I pass it a value from the $hour-array variable, it does not display the corresponding total count.

XML data

    <Records reportTime24h="18:14" reportTime="06:14:55PM" reportDate="2020-11-12" reportTitle="Samples per time of day v2">
      <vars>
      <date>2020-11-12</date>
      </vars>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>07 AM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>08 AM</RegisterHour>
      <Total>15</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>08 AM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>08 AM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Plasma</TubeName>
      <RegisterHour>08 AM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>09 AM</RegisterHour>
      <Total>23</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>09 AM</RegisterHour>
      <Total>15</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>09 AM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>09 AM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>09 AM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>09 AM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>10 AM</RegisterHour>
      <Total>25</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>10 AM</RegisterHour>
      <Total>8</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>10 AM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>11 AM</RegisterHour>
      <Total>30</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>11 AM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>11 AM</RegisterHour>
      <Total>18</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>11 AM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>11 AM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>11 AM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>3</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>29</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>24</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>3</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Plasma</TubeName>
      <RegisterHour>12 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>01 PM</RegisterHour>
      <Total>32</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>01 PM</RegisterHour>
      <Total>33</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>01 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>01 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>01 PM</RegisterHour>
      <Total>5</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>01 PM</RegisterHour>
      <Total>3</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>36</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>13</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>38</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>8</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Plasma</TubeName>
      <RegisterHour>02 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>23</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>29</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>12</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>5</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>5</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Plasma</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>3</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Urine</TubeName>
      <RegisterHour>03 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>FAST_PLA</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>6</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>16</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>17</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Plasma</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>2HR_PLASMA</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>1HR_PLASMA</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Urine</TubeName>
      <RegisterHour>04 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Plasma</TubeName>
      <RegisterHour>05 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>05 PM</RegisterHour>
      <Total>3</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>05 PM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>06 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>06 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>09 PM</RegisterHour>
      <Total>5</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>09 PM</RegisterHour>
      <Total>4</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>10 PM</RegisterHour>
      <Total>11</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>10 PM</RegisterHour>
      <Total>11</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>10 PM</RegisterHour>
      <Total>1</Total>
      </Record>
      <Record>
      <TubeName>Wholeblood Haematology</TubeName>
      <RegisterHour>11 PM</RegisterHour>
      <Total>2</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Serum</TubeName>
      <RegisterHour>11 PM</RegisterHour>
      <Total>3</Total>
      </Record>
      <Record>
      <TubeName>Chemistry Wholeblood</TubeName>
      <RegisterHour>11 PM</RegisterHour>
      <Total>1</Total>
      </Record>
  </Records>

XSLT

   <?xml version="1.0" encoding="UTF-8"?>
   <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="key-tube" match="Record" use="TubeName"/>
    <xsl:key name="key-hour" match="Record" use="RegisterHour"/>
    <xsl:key name="key-tube-hour" match="Record" use="concat(TubeName,'::',RegisterHour)"/>

    <xsl:key name="key-hour-list" match="RegisterHour" use="."/>
    <xsl:key name="key-tube-list" match="TubeName" use="."/>
    <!--<xsl:variable name="unique-hour-list" select="//RegisterHour[generate-id() = generate-id(key('key-hour-list', .)[1])]"/>-->
    <xsl:variable name="unique-tube-list" select="//TubeName[generate-id() = generate-id(key('key-tube-list', .)[1])]"/>

    <!--Hour Array -->
    <xsl:variable name="hours">
       <hour>01 AM</hour>
       <hour>02 AM</hour>
       <hour>03 AM</hour>
       <hour>04 AM</hour>
       <hour>05 AM</hour>
       <hour>06 AM</hour>
       <hour>07 AM</hour>
       <hour>08 AM</hour>
       <hour>09 AM</hour>
       <hour>10 AM</hour>
       <hour>11 AM</hour>
       <hour>12 AM</hour>
       <hour>01 PM</hour>
       <hour>02 PM</hour>
       <hour>03 PM</hour>
       <hour>04 PM</hour>
       <hour>05 PM</hour>
       <hour>06 PM</hour>
       <hour>07 PM</hour>
       <hour>08 PM</hour>
       <hour>09 PM</hour>
       <hour>10 PM</hour>
       <hour>11 PM</hour>
       <hour>12 PM</hour>
    </xsl:variable>
    <xsl:variable name="hour-array" select="exsl:node-set($hours)" xmlns:exsl="http://exslt.org/common"/>
    
    <xsl:template match="/Records">
       <html>
          <head>
             <title>
                <xsl:value-of select="/Records/@reportTitle"/>_<xsl:value-of select="/Records/vars/date"/>
             </title>
             <style>
                 body            { font-family: monospace;       }
                 table           { border-collapse: collapse; font-size: 8pt; width: 100%;   }
                 td,th           { padding: 3px; border: 1px solid gainsboro;}
                 th.day          { width: 25px; }
                 h1,h2,h3,h4,h5  { margin: 0 0 5px 0; }
                 h3              { color: gray; }
                 thead,tfoot           { background-color: whitesmoke; }
                 tbody th,
                 tbody td        { text-align: right; }
                 tbody td.test   { text-align: left; }
                 tbody td.area   { text-align: left; font-weight: bold; text-decoration: underline; }
                 img.logo        { float: right; width: 70px; }
                 header          { clear:both; margin-bottom: 10px; border-bottom: 1px solid gray; padding-bottom: 10px;}
                 tfoot th         { text-align:right;}
                 
                 .red-10          {background-color: #F3DFDB}
                 .red-30          {background-color: #F1C8BF}
                 .red-50          {background-color: #EE8873}
                 .red-70          {background-color: #EF6E53}
                 .red-90          {background-color: #EB411E}
             </style>
          </head>
          <body>
           <h1><xsl:value-of select="/Records/@reportTitle"/></h1>
           <h3><xsl:value-of select="/Records/vars/date"/>  </h3>
           
             <table>
                <thead>
                   <tr>
                      <th style="text-align:left">Time of Day</th>
                         <xsl:apply-templates select="$unique-tube-list" mode="day-list"/>
                      <th>Total</th>
                    </tr>   
                 </thead>
                 <tbody>
                     <xsl:apply-templates select="$hour-array" mode="test-list"/>
                 </tbody>
                 
                <tfoot>
                   <tr>
                      <th>Totals</th>
                      <xsl:apply-templates select="$unique-tube-list" mode="day-totals"/>  
                      <th>
                          <xsl:value-of select="format-number(sum(Record/Total),'###,###')" />
                      </th>
                   </tr>
                </tfoot>
             </table>
          </body>
       </html>
    </xsl:template>

    <xsl:template match="TubeName" mode="day-list">
        <th class="day"><xsl:value-of select="."/></th>
    </xsl:template>
    
    <xsl:template match="TubeName" mode="day-totals">
        <th><xsl:value-of select="format-number(sum(key('key-tube',.)/Total),'###,###')"/></th>
    </xsl:template>
    
    <xsl:template match="hour" mode="test-list">
        <xsl:variable name="h" select="."/>
        <xsl:variable name="grand-total" select="sum(/Records/Record/Total)" />
        <xsl:variable name="row-total" select="sum(key('key-hour',.)/Total)" />
        <xsl:variable name="pct" select="($row-total div $grand-total)*100" />
        <tr>
           <xsl:choose>
              <xsl:when test="$pct &gt; 50">
                  <xsl:attribute name="class">red-90</xsl:attribute>
               </xsl:when>
               <xsl:when test="$pct &gt; 40">
                  <xsl:attribute name="class">red-70</xsl:attribute>
               </xsl:when>
               <xsl:when test="$pct &gt; 30">
                  <xsl:attribute name="class">red-50</xsl:attribute>
               </xsl:when>
               <xsl:when test="$pct &gt; 20">
                  <xsl:attribute name="class">red-30</xsl:attribute>
               </xsl:when>
               <xsl:when test="$pct &gt; 10">
                  <xsl:attribute name="class">red-10</xsl:attribute>
               </xsl:when>
           </xsl:choose>
           
             <td class="test">
               <xsl:value-of select="$h"/> (<xsl:value-of select="key('key-hour',$h)"/>)
             </td>
            <xsl:for-each select="$unique-tube-list">
                <xsl:variable name="total" select="key('key-tube-hour',concat(current(),'::',$h))/Total"/>
                <xsl:choose>
                    <xsl:when test="$total">
                        <td><xsl:value-of select="format-number($total,'###,###')" /></td>
                    </xsl:when>
                    <xsl:otherwise>
                        <td>0</td>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:for-each>
            <td>
                <xsl:value-of select="format-number(sum(key('key-hour',$h)/Total),'###,###')" />
            </td>
        </tr>    
     </xsl:template>
   </xsl:stylesheet>

This results in the total column not been populated with the actual sum of totals. Here is what that looks like.

Results outuput

Here is a fiddle of the result for you to play with. https://xsltfiddle.liberty-development.net/pNvtBGk


Solution

  • I would suggest to change the context by doing

       <xsl:template match="hour" mode="test-list">
           <xsl:apply-templates select="$main-doc" mode="test-list">
               <xsl:with-param name="h" select="."/>
           </xsl:apply-templates>
       </xsl:template>
       
       <xsl:template match="/" mode="test-list">
           <xsl:param name="h"/>
           <xsl:variable name="grand-total" select="sum(/Records/Record/Total)" />
           <xsl:variable name="row-total" select="sum(key('key-hour', $h)/Total)" />
           <xsl:variable name="pct" select="($row-total div $grand-total)*100" />
           <tr>
              <xsl:choose>
                 <xsl:when test="$pct &gt; 50">
                     <xsl:attribute name="class">red-90</xsl:attribute>
                  </xsl:when>
                  <xsl:when test="$pct &gt; 40">
                     <xsl:attribute name="class">red-70</xsl:attribute>
                  </xsl:when>
                  <xsl:when test="$pct &gt; 30">
                     <xsl:attribute name="class">red-50</xsl:attribute>
                  </xsl:when>
                  <xsl:when test="$pct &gt; 20">
                     <xsl:attribute name="class">red-30</xsl:attribute>
                  </xsl:when>
                  <xsl:when test="$pct &gt; 10">
                     <xsl:attribute name="class">red-10</xsl:attribute>
                  </xsl:when>
              </xsl:choose>
              
                <td class="test">
                  <xsl:value-of select="$h"/> (<xsl:value-of select="key('key-hour',$h)"/>)
                </td>
               <xsl:for-each select="$unique-tube-list">
                   <xsl:variable name="total" select="key('key-tube-hour',concat(current(),'::',$h))/Total"/>
                   <xsl:choose>
                       <xsl:when test="$total">
                           <td><xsl:value-of select="format-number($total,'###,###')" /></td>
                       </xsl:when>
                       <xsl:otherwise>
                           <td>0</td>
                       </xsl:otherwise>
                   </xsl:choose>
               </xsl:for-each>
               <td>
                   <xsl:value-of select="format-number(sum(key('key-hour',$h)/Total),'###,###')" />
               </td>
           </tr>    
       </xsl:template>
    

    Needs <xsl:variable name="main-doc" select="/"/> and where you process the hours change the apply-templates to <xsl:apply-templates select="$hour-array/hour" mode="test-list"/>

    https://xsltfiddle.liberty-development.net/pNvtBGk/2