QnA
QnA

Reputation: 75

Getting the Remainder using XSLT

1st time that i'll be writing this is in xslt (can be xslt 2 or 3). I would need the sum of all remainders. does anyone encountered this already? thank you.

here's the requirement:

2 IDs fields called id1 and id2. Each field has 6 chars (incl. space and decimal)

LEFT ALIGN:
    id1: 111   (3 spaces included here after 111) 

RIGHT ALIGN:
    id2: 123.50  (one decimal here)

I need to get first value A, B, C, D whereas:

    VALUE A: add up id1 from position 1 - 3
    VALUE B: add up id1 from position 4 - 6

    VALUE C: add up id2 from position 1 - 3
    VALUE D: add up id2 from position 4 - 6

NOTE: value of space is 20, and decimal '.' is 15.

Sample Computation

id1: 111   (3 spaces included here)
    VALUE A: 3 (1+1+1) AND
    VALUE B: 60 (20+20+20)

id2: 123.50  (one decimal here)
    VALUE C: 5 (1+2+3) AND
    VALUE D: 20 (15+5)
Remainder= mod(abs((B+D)-(A+C)),13)

7 

Sum of all remainders: 7


Example 2:

Row 1:
LEFT ALIGN:
id1: 111   (3 spaces included here after 111) 

RIGHT ALIGN
id2: 123.50  

Row 2:
LEFT ALIGN:
id1: 321.50   

RIGHT ALIGN
id2:   222  (3 spaces before 111)

Row 1:
Value A: 3 (1+1+1)
Value B: 60 (20+20+20)
Value C: 6 (1+2+3)
Value D: 20 (15+5)

Row 2:
Value A: 6 (3+2+1)
Value B: 20 (15+5)
Value C:60 (20+20+20 
Value D: 6 (2+2+2)

Remainder= mod(abs((B+D)-(A+C)),13)

Row1: Remainder = mod(abs((60+20)-(3+6)),13)
Remainder = 6

Row 2:Remainder=mod(abs((20+6)-(6+6)),13)
Remainder = 1


Sum of all remainders: 7 (6+1) 

7 or the sum of all remainders will be included in the final output file.

Thanks for the help.

Upvotes: 0

Views: 384

Answers (1)

Martin Honnen
Martin Honnen

Reputation: 167716

With XSLT 3, one way might be to use accumulators:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:mf="http://example.com/mf"
    exclude-result-prefixes="#all"
    version="3.0">

  <xsl:output method="text" item-separator="&#10;"/>

  <xsl:mode on-no-match="shallow-skip" use-accumulators="#all"/>

  <xsl:accumulator name="A" as="xs:integer?" initial-value="()">
      <xsl:accumulator-rule
        match="fields/data/id1"
        phase="end"
        select="accumulator-after('values') => subsequence(1, 3) => sum()"/>
  </xsl:accumulator>

  <xsl:accumulator name="B" as="xs:integer?" initial-value="()">
      <xsl:accumulator-rule
        match="fields/data/id1"
        phase="end"
        select="accumulator-after('values') => subsequence(4, 3) => sum()"/>
  </xsl:accumulator>

  <xsl:accumulator name="C" as="xs:integer?" initial-value="()">
      <xsl:accumulator-rule
        match="fields/data/id2"
        phase="end"
        select="accumulator-after('values') => subsequence(1, 3) => sum()"/>
  </xsl:accumulator>

  <xsl:accumulator name="D" as="xs:integer?" initial-value="()">
      <xsl:accumulator-rule
        match="fields/data/id2"
        phase="end"
        select="accumulator-after('values') => subsequence(4, 3) => sum()"/>
  </xsl:accumulator>

  <xsl:accumulator name="values" as="xs:integer*" initial-value="()">
      <xsl:accumulator-rule
        match="data" select="()"/>
      <xsl:accumulator-rule
        match="data/id1/text() | data/id2/text()"
        select="analyze-string(.,'.')//*:match ! (
                  if (. = '.')
                  then 15
                  else if (. = ' ')
                  then 20
                  else xs:integer(.))"/>
  </xsl:accumulator>

  <xsl:accumulator name="remainder" as="xs:integer?" initial-value="()">
      <xsl:accumulator-rule
        match="fields/data" select="()"/>
      <xsl:accumulator-rule
        match="fields/data"
        phase="end"
        select="abs((accumulator-after('B') + accumulator-after('D')) -
                     (accumulator-after('A') + accumulator-after('C'))) mod 13"/>
  </xsl:accumulator>

  <xsl:accumulator name="remainder-sum" as="xs:integer?" initial-value="()">
      <xsl:accumulator-rule
        match="fields" select="0"/>
      <xsl:accumulator-rule
        match="fields/data"
        select="$value + accumulator-after('remainder')"/>
  </xsl:accumulator>


  <xsl:output method="text" item-separator="&#10;"/>

  <xsl:template match="fields">
      <xsl:apply-templates/>
      <xsl:sequence
        select="accumulator-after('remainder-sum')"/>
  </xsl:template>

</xsl:stylesheet>

https://xsltfiddle.liberty-development.net/bwe3bG/1

Of course you can only apply the code to the last element with

  <xsl:template match="fields[last()]">
      <xsl:apply-templates/>
      <xsl:sequence
        select="accumulator-after('remainder-sum')"/>
  </xsl:template>

(instead of the <xsl:template match="fields"> used earlier). See https://xsltfiddle.liberty-development.net/bwe3bG/2

Upvotes: 0

Related Questions