XSLT 1.0 accumulation, summing, multiplication
I have a small fixed set of nodes: <a>
, <b>
, <c>
, <d>
. Each node can have a value of either 1 or 0.
Also, each node has a weight: 1, 2, 3, 4, respectively. Node attributes are not used.
How can I sum the value of each node multiplied by its weight using XSLT 1.0? Example:
<a>0</a>
<b>1</b>
<c>0</c&开发者_JS百科gt;
<d>1</d>
Sum: 6
<a>1</a>
<b>1</b>
<c>0</c>
<d>0</d>
Sum: 3
<a>0</a>
<b>1</b>
<c>1</c>
<d>1</d>
Sum: 9
This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/*">
<xsl:call-template name="sumWeighted"/>
</xsl:template>
<xsl:template name="sumWeighted">
<xsl:param name="pList" select="*"/>
<xsl:param name="pIndex" select="1"/>
<xsl:param name="pAccum" select="0"/>
<xsl:choose>
<xsl:when test="$pIndex > count($pList)">
<xsl:value-of select="$pAccum"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sumWeighted">
<xsl:with-param name="pList" select="$pList"/>
<xsl:with-param name="pIndex"
select="$pIndex+1"/>
<xsl:with-param name="pAccum"
select="$pAccum+$pList[$pIndex]*$pIndex"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied to the provided XML documents:
<t>
<a>0</a>
<b>1</b>
<c>0</c>
<d>1</d>
</t>
,
<t>
<a>1</a>
<b>1</b>
<c>0</c>
<d>0</d>
</t>
and
<t>
<a>0</a>
<b>1</b>
<c>1</c>
<d>1</d>
</t>
produces the wanted results, respectively:
6
3
9
Reference:
If you want to get your hands dirty with really complicated math calculations using XSLT, see this:
http://fxsl.sourceforge.net/articles/xslCalculator/The%20FXSL%20Calculator.html
XPath 2.0 / XSLT 2.0 Solution:
Use just this XPath 2.0 one-liner:
sum(/*/*/(number()*position()))
Not really elegant, but that's what came to my mind.
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my"
exclude-result-prefixes="my">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<my:node-weight>
<node name="a" weight="1"/>
<node name="b" weight="2"/>
<node name="c" weight="3"/>
<node name="d" weight="4"/>
</my:node-weight>
<xsl:template match="root">
<xsl:apply-templates select="*[1]" mode="sum"/>
</xsl:template>
<xsl:template match="*" mode="sum">
<xsl:param name="sumNumber" select="0"/>
<xsl:variable name="thisWeight" select="document('')/*/my:node-weight/node[@name = local-name(current())]/@weight"/>
<xsl:choose>
<xsl:when test="following-sibling::*">
<xsl:apply-templates select="following-sibling::*[1]" mode="sum">
<xsl:with-param name="sumNumber" select="$sumNumber + $thisWeight * number()"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$sumNumber + $thisWeight * number()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to the following well-formed documents:
<root>
<a>0</a>
<b>1</b>
<c>0</c>
<d>1</d>
</root>
Result is 6
<root>
<a>1</a>
<b>1</b>
<c>0</c>
<d>0</d>
</root>
Result is 3
<root>
<a>0</a>
<b>1</b>
<c>1</c>
<d>1</d>
</root>
Result is 9
精彩评论