bartneck
bartneck

Reputation: 37

XSLT sum of multiplication with ancestor

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

Answers (1)

Martin Honnen
Martin Honnen

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

Related Questions