tags:

views:

530

answers:

6

Need to add alternate digits in a number receiving from XML file using XSLT, for instance If I am receiving a 123456789, I need to calculate alternate digit sum from right most using XSLT function, can i have any suggestions on this ?

Thanks, Laxmikanth.S

A: 

Yes; don't use XSLT or XML for this, it doesn't make sense.

Noon Silk
even though it is fixed length number, I mean 10 digit number ?
Laxmikanth Samudrala
I am using a saxon parser and it is having saxon extensions but i am very new this parser, any suggestions ?
Laxmikanth Samudrala
Not other than that which I've already provided. Perhaps someone else will have some comments for you.
Noon Silk
+2  A: 

This is extremely easy to do with XSLT 2.0 (actually just with a single XPath 2.0 expression):

The following XSLT transformation:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
    <xsl:output method="text"/>

    <xsl:template match="/">
      <xsl:sequence select=
      "sum(for $n in reverse(string-to-codepoints('123456789'))
                                     [position() mod 2 eq 1]
             return
               $n  - string-to-codepoints('0') 
           )
      "/>
    </xsl:template>
</xsl:stylesheet>

when aplied on any XML document (not used), produces the correct result:

25

Do note the use of the XPath 2.0 functions: string-to-codepoints(), codepoints-to-string() and reverse().


UPDATE: A similar, but a little simpler expression is:

sum(for $n in reverse(string-to-codepoints('123456789'))
                              [position() mod 2 eq 1]
      return
         xs:integer(codepoints-to-string($n))
    )

In order for this expression to compile, the xs prefix must be bound to the namespace: "http://www.w3.org/2001/XMLSchema"

Dimitre Novatchev
A: 

If you have the EXSL extensions (http://exslt.org) you can just do something like:

sum(str:tokenize($x,'')[position() mod 2 = 0])

if not you can do something similar by looping with the substring function.

jp
A: 

If you are not able to use XSLT 2.0, it is still possible using XSLT 1.0. One way could be be recursively calling a named template:

<xsl:template match="number">
 <xsl:call-template name="sumdigits">
  <xsl:with-param name="number" select="." />
 </xsl:call-template>
</xsl:template>

<xsl:template name="sumdigits">
 <xsl:param name="number" />
 <xsl:param name="runningtotal">0</xsl:param>
 <xsl:choose>
  <xsl:when test="string-length($number) = 0">
   <xsl:value-of select="$runningtotal" />
  </xsl:when>
  <xsl:otherwise>
   <xsl:variable name="lastdigit" select="substring($number, string-length($number), 1)" />
   <xsl:variable name="newrunningtotal" select="number($runningtotal) + number($lastdigit)" />
   <xsl:call-template name="sumdigits">
    <xsl:with-param name="number" select="substring($number, 1, string-length($number) - 2)" />
    <xsl:with-param name="runningtotal" select="$newrunningtotal" />
   </xsl:call-template>
  </xsl:otherwise>
 </xsl:choose>
</xsl:template>

It works by getting the last digit in the input, adds it on to a 'running total' and recursively calling the named template with the last two digits of the input removed. When there are no digits left, the running total can be returned.

This XSLT fragment assumes the input is in the XML within the element named 'number'

   <number>123456789</number>

The result should be 25. As you can see, doing it in XSLT2.0 would be much nicer.

Tim C
+1  A: 

For an XSLT 1.0 solution I would use FXSL.

This transformation first reverses the string, using the "str-reverse" template, then performs two calls to the "str-foldl" template and does the work:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dvc-foldl-func="dvc-foldl-func"
exclude-result-prefixes="xsl dvc-foldl-func"
>
   <xsl:import href="str-foldl.xsl"/>
   <xsl:import href="strReverse.xsl"/>

   <dvc-foldl-func:delEven/>
   <dvc-foldl-func:add/>

   <xsl:variable name="vFoldlDelEven" 
        select="document('')/*/dvc-foldl-func:delEven[1]"/>
   <xsl:variable name="vFoldlAdd" 
        select="document('')/*/dvc-foldl-func:add[1]"/>

    <xsl:output method="text"/>

    <xsl:template match="/">
       <xsl:variable name="vReversed">
         <xsl:call-template name="strReverse">
           <xsl:with-param name="pStr" select="'123456789'"/>
         </xsl:call-template>
       </xsl:variable>

       <xsl:variable name="vOddDigits">
          <xsl:call-template name="str-foldl">
            <xsl:with-param name="pFunc" select="$vFoldlDelEven"/>
            <xsl:with-param name="pStr" select="$vReversed"/>
            <xsl:with-param name="pA0" select="''"/>
          </xsl:call-template>
      </xsl:variable>

      <xsl:call-template name="str-foldl">
        <xsl:with-param name="pFunc" select="$vFoldlAdd"/>
        <xsl:with-param name="pStr" select="$vOddDigits"/>
        <xsl:with-param name="pA0" select="0"/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template match="dvc-foldl-func:add">
         <xsl:param name="arg1" select="0"/>
         <xsl:param name="arg2" select="0"/>

         <xsl:value-of select="$arg1 + $arg2"/>
    </xsl:template>

    <xsl:template match="dvc-foldl-func:delEven">
         <xsl:param name="arg1"/>
         <xsl:param name="arg2"/>

         <xsl:copy-of select=
           "concat($arg1, 
                   $arg2 * (string-length($arg1) mod 2 = 0)
                  )
           "/>
    </xsl:template>
</xsl:stylesheet>

When the above transformation is applied on any source XML (ignored), the wanted result is obtained:

25

Do note:

  1. The first template call reverses the string.
  2. The second template call replaces every even-th-placed digit of the reversed string with 0.
  3. The last template call produces the sum of all digits in the string produced by the second template call
Dimitre Novatchev
+1  A: 

Just in case you are doing this because you need to calculate the Luhn (mod 10) checksum for something, this was published on IBM developerWorks.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:regexp="http://exslt.org/regular-expressions" xmlns:tns="http://www.example.org/tns" xmlns:dp="http://www.datapower.com/extensions" extension-element-prefixes="dp" exclude-result-prefixes="dp regexp" xmlns:fn="http://www.w3.org/2005/02/xpath-functions"&gt;
  <xsl:template match="/">
    <xsl:call-template name="recLuhn">
      <xsl:with-param name="index" select="1"/>
      <xsl:with-param name="parity" select="string-length(CreditCard) mod 2"/>
    </xsl:call-template>
  </xsl:template>
  <xsl:template name="recLuhn">
    <xsl:param name="index"/>
    <xsl:param name="parity"/>
    <xsl:choose>
      <xsl:when test="$index &lt;= string-length(CreditCard) ">
        <xsl:variable name="sum">
          <xsl:call-template name="recLuhn">
            <xsl:with-param name="index" select="$index + 1"/>
            <xsl:with-param name="parity" select="$parity"/>
          </xsl:call-template>
        </xsl:variable>
        <xsl:variable name="thisSum">
          <xsl:choose>
            <xsl:when test="$index mod 2 != $parity">
              <xsl:choose>
                <xsl:when test="substring(CreditCard, $index, 1)*2 &gt; 9">
                  <xsl:value-of select="(number(substring(CreditCard, $index, 1)) * 2) - 9"/>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:value-of select="number(substring(CreditCard, $index, 1)) * 2"/>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="number(substring(CreditCard, $index, 1))"/>
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <xsl:value-of select="$thisSum + $sum"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="0"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>
</xsl:stylesheet>
lavinio