Reputation: 37
I am struggling with calculating the sum of a series of multiplications in XSLT. Any help would be greatly appreciated.
I need to calculate the length of the XML swimming program below. From the context of <program>
I can sum up all the <lengthAsDistance>
with this
<xsl:value-of select="sum(descendant::lengthAsDistance)"/>
Which results in
101+102+103+104+105+106+107+108+8=844
But I need to multiply the length of each <lengthAsDistance>
with any ancestors' child element <repetitionCount>
The right answer should be:
101+102+103+4*104+5*104+6*2*106+6*2*107+6*108+8=4459
<program>
<instruction>
<lengthAsDistance>101</lengthAsDistance>
</instruction>
<instruction>
<lengthAsDistance>102</lengthAsDistance>
</instruction>
<instruction>
<lengthAsDistance>103</lengthAsDistance>
</instruction>
<instruction>
<repetition>
<repetitionCount>4</repetitionCount>
<instruction>
<lengthAsDistance>104</lengthAsDistance>
</instruction>
</repetition>
</instruction>
<instruction>
<repetition>
<repetitionCount>5</repetitionCount>
<instruction>
<lengthAsDistance>105</lengthAsDistance>
</instruction>
</repetition>
</instruction>
<instruction>
<repetition>
<repetitionCount>6</repetitionCount>
<instruction>
<repetition>
<repetitionCount>2</repetitionCount>
<instruction>
<lengthAsDistance>106</lengthAsDistance>
</instruction>
<instruction>
<lengthAsDistance>107</lengthAsDistance>
</instruction>
</repetition>
</instruction>
<instruction>
<lengthAsDistance>108</lengthAsDistance>
</instruction>
</repetition>
</instruction>
<instruction>
<lengthAsDistance>8</lengthAsDistance>
</instruction>
</program>
Any help would be greatly appreciated. Thank you!
Upvotes: 0
Views: 111
Reputation: 167706
In XSLT 3 with higher-order functions (as supported with Saxon 10/11 HE or commercial PE/EE since 9.8) or Saxon-JS 2 you can do e.g.
<xsl:template match="program" expand-text="yes">
<total>{sum(
for $l in //lengthAsDistance
return
$l * fold-left($l/ancestor::repetition/repetitionCount, 1, function($a, $r) { $a * $r })
)}</total>
</xsl:template>
XSLT/XPath 2.0 doesn't have the higher-order fold-left
function but you can of course implement your own recursive function with xsl:function
e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns:mf="http://example.com/mf">
<xsl:function name="mf:product" as="xs:decimal">
<xsl:param name="numbers" as="xs:decimal*"/>
<xsl:sequence
select="if (empty($numbers))
then 1
else $numbers[1] * mf:product($numbers[position() gt 1])"/>
</xsl:function>
<xsl:template match="program">
<total>
<xsl:value-of
select="sum(
for $l in //lengthAsDistance
return
$l * mf:product($l/ancestor::repetition/repetitionCount)
)"/>
</total>
</xsl:template>
Upvotes: 2