AquaVitale
AquaVitale

Reputation: 40

XSLT merge multiple entries and add up numbers

ich have following XML:

<item>
   <number>1</number>
   <value>2</value>
</item>
<item>
   <number>2</number>
   <value>22</value>
</item>
<item>
   <number>1</number>
   <value>4</value>
</item>

I want to transform the xml with xslt 1.0 to this:

<item>
   <number>1</number>
   <value>6</value>
</item>
<item>
   <number>2</number>
   <value>22</value>
</item>

The values from the same item number have to be added up and in the end i want to merge the elements of the same item. There can be n multible items with the same number. In this example the item with the number 1 is two times in the xml so those two will be one entry and the values(2,4) will be added up to 6.

Upvotes: 2

Views: 621

Answers (1)

Daniel Haley
Daniel Haley

Reputation: 52878

This is a very basic grouping question. In XSLT 1.0, grouping is done by using the Muenchian method.

Start with an identity transform...

<xsl:template match="@*|node()">
  <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:copy>
</xsl:template>

Group all of the item's by number by creating an xsl:key matching item using number...

<xsl:key name="items" match="item" use="number"/>

From the context of the parent of all item's (using doc as an example), output a copy of the element and apply templates to the first item in the group (and any attributes)...

<xsl:template match="doc">
  <xsl:copy>
    <xsl:apply-templates select="@*|item[count(.|key('items',number)[1])=1]"/>
  </xsl:copy>
</xsl:template>

Add a template that matches item and output a copy of it. Apply templates to any attributes and the number element. Then create a new value element with the sum of all item's matching that key...

<xsl:template match="item">
  <xsl:copy>
    <xsl:apply-templates select="@*|number"/>
    <value><xsl:value-of select="sum(key('items',number)/value)"/></value>
  </xsl:copy>
</xsl:template>

Full example...

XML Input (added a single root element doc to make the input well formed)

<doc>
    <item>
        <number>1</number>
        <value>2</value>
    </item>
    <item>
        <number>2</number>
        <value>22</value>
    </item>
    <item>
        <number>1</number>
        <value>4</value>
    </item>
</doc>

XSLT 1.0

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

  <xsl:key name="items" match="item" use="number"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="doc">
    <xsl:copy>
      <xsl:apply-templates select="@*|item[count(.|key('items',number)[1])=1]"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="item">
    <xsl:copy>
      <xsl:apply-templates select="@*|number"/>
      <value><xsl:value-of select="sum(key('items',number)/value)"/></value>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>

XML Output

<doc>
   <item>
      <number>1</number>
      <value>6</value>
   </item>
   <item>
      <number>2</number>
      <value>22</value>
   </item>
</doc>

Upvotes: 4

Related Questions