Search code examples
xsltxslt-2.0

How to group and sum values in XSLT


For each "agency" node I need to find the "stmt" elements that have the same key1, key2, key3 values and output just one "stmt" node with the "comm" and "prem" values summed together. For any "stmt" elements within that "agency" that don't match any other "stmt" elements based on key1, key2 and key3 I need to output them as is. So after transformation the first "agency" node would only have two "stmt" nodes (one summed) and the second "agency" node would be passed as is because the keys don't match. XSLT 1.0 or 2.0 solutions are ok...though my stylesheet is currently 1.0. Note that the agency nodes could have any number of "stmt" elements that have matching keys which need to be grouped and summed and any number that don't.

<statement>
<agency>
    <stmt>
        <key1>1234</key1>
        <key2>ABC</key2>
        <key3>15.000</key3>
        <comm>75.00</comm>
        <prem>100.00</prem>
    </stmt>
    <stmt>
        <key1>1234</key1>
        <key2>ABC</key2>
        <key3>15.000</key3>
        <comm>25.00</comm>
        <prem>200.00</prem>
    </stmt>
    <stmt>
        <key1>1234</key1>
        <key2>ABC</key2>
        <key3>17.50</key3>
        <comm>25.00</comm>
        <prem>100.00</prem>
    </stmt>
</agency>
<agency>
    <stmt>
        <key1>5678</key1>
        <key2>DEF</key2>
        <key3>15.000</key3>
        <comm>10.00</comm>
        <prem>20.00</prem>
    </stmt>
    <stmt>
        <key1>5678</key1>
        <key2>DEF</key2>
        <key3>17.000</key3>
        <comm>15.00</comm>
        <prem>12.00</prem>
    </stmt>
</agency>


Solution

  • And an XSLT 2.0 solution:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xs="http://www.w3.org/2001/XMLSchema"
     exclude-result-prefixes="xs"
     >
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="node()|@*">
       <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
       </xsl:copy>
     </xsl:template>
    
     <xsl:template match="agency">
      <agency>
       <xsl:for-each-group select="stmt" group-by=
        "concat(key1, '+', key2, '+', key3)">
    
        <stmt>
          <xsl:copy-of select=
           "current-group()[1]/*[starts-with(name(),'key')]"/>
    
           <comm>
             <xsl:value-of select="sum(current-group()/comm)"/>
           </comm>
           <prem>
             <xsl:value-of select="sum(current-group()/prem)"/>
           </prem>
        </stmt>
       </xsl:for-each-group>
      </agency>
     </xsl:template>
    </xsl:stylesheet>