I. Here is a simple, forward-only solution -- do note that no reverse axis is used and the time complexity is just O(N) and the space complexity is just O(1).
This is probably the simplest and fastest of all presented solutions:
No monstrous complexity or grouping is required at all ...
No variables, no keys (and no space taken for caching key->values), no sum() ...
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*"><xsl:apply-templates select="*[1]"/></xsl:template>
<xsl:template match="entry[field/@type = 'num']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat(field/@value, '
')"/>
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="pAccum" select="$pAccum+field/@value"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="entry[field/@type = 'summary']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat('#', $pAccum, '#
')"/>
<xsl:apply-templates select="following-sibling::entry[1]"/>
</xsl:template>
</xsl:stylesheet>
This is an example of a streaming transformation -- it doesn't require the complete XML document tree to be present in memory and can be used to process documents of indefinite or infinite length.
When the transformation is applied on the provided source XML document:
<list>
<entry>
<field type="num" value="189.5" />
</entry>
<entry>
<field type="num" value="1.5" />
</entry>
<entry>
<field type="summary" />
</entry>
<entry>
<field type="num" value="9.5" />
</entry>
<entry>
<field type="num" value="11" />
</entry>
<entry>
<field type="num" value="10" />
</entry>
<entry>
<field type="summary" />
</entry>
</list>
the wanted, correct result is produced:
189.5
1.5
#191#
9.5
11
10
#30.5#
II. Update
The transformation above when run on sufficiently-big XML documents and with XSLT processors that don't optimize tail-recursion, causes stack overflow, due to a long chain of <xsl:apply-templates>
Below is another transformation, which doesn't cause stack overflow even with extremely big XML documents. Again, no reverse axes, no keys, no "grouping", no conditional instructions, no count()
, no <xsl:variable>
...
And, most importantly, compared with the "efficient", key-based Muenchian grouping, this transformation takes only 61% of the time of the latter, when run on an XML document having 105 000 (105 thousand) lines:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select=
"*[1] | entry[field/@type = 'summary']/following-sibling::*[1]"/>
</xsl:template>
<xsl:template match="entry[field/@type = 'num']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat(field/@value, '
')"/>
<xsl:apply-templates select="following-sibling::entry[1]">
<xsl:with-param name="pAccum" select="$pAccum+field/@value"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="entry[field/@type = 'summary']">
<xsl:param name="pAccum" select="0"/>
<xsl:value-of select="concat('#', $pAccum, '#
')"/>
</xsl:template>
</xsl:stylesheet>
Additionally, this transformation can be speeded to take less than 50% (that is, make it more than twice as fast) of the time taken by the Muenchian grouping transformation, by replacing every element name by just *
A lesson for us all to learn: A non-key solution sometimes can be more efficient than a key-based one.