[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Transform and Sum (Was Re: sum function)

Subject: Transform and Sum (Was Re: sum function)
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Fri, 16 Nov 2001 14:08:52 -0800 (PST)
xsl sum function
> How do I use the sum function on xml such as:
> 
> <Amount>12,345.12</Amount>
> <Amount>132,345.12</Amount>
> <Amount>2,345.12</Amount>
> 
> If I use Total Price = <xsl:value-of select="sum(//Amount)"/> I get the
> result NaN. However, if the data does not contain ',' the sum works
> correctly.

The functional solution to this problem is:

transform-and-sum funTrans ls  =  foldl (add.funTrans) 0 ls

In case we already have the XSLT implementation of the above function, we can simply
call it, providing as a parameter our own funTrans function:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:func-transform="f:func-transform"
exclude-result-prefixes="xsl func-transform"
>
   <xsl:import href="transform-and-sum.xsl"/>

   <xsl:output method="text"/>
   
   <func-transform:func-transform/>

    <xsl:template match="/">
      <xsl:call-template name="transform-and-sum">
        <xsl:with-param name="pFuncTransform" 
                        select="document('')/*/func-transform:*[1]"/>
        <xsl:with-param name="pList" select="/*/*"/>
      </xsl:call-template>
    </xsl:template>
    
    <xsl:template match="func-transform:*">
      <xsl:param name="arg" select="0"/>
      <xsl:value-of select="translate($arg, ',', '')"/>
    </xsl:template>

</xsl:stylesheet>

And in case the source xml doc is the following:

<t>
  <Amount>12,345.12</Amount>
  <Amount>132,345.12</Amount>
  <Amount>2,345.12</Amount>
</t>

we get the result:

147035.36

We can even implement our own eliminateCommas function, although this would be much
less efficient than using translate() directly.

Why would we implement transform-and-sum for such seemingly very simple case?

The answer is: because once implemented, it can be re-used to solve many other
problems, some of them quite complex, for which direct XSLT support (like
translate()) is not available.

Here's an example: find the sum of hexadecimal numbers.

We're using the same function transform-and-sum, passing to it this time another
function as parameter -- this one converts the hexadecimal representation of a
number into its decimal representation. here's how the code looks like in this case:

<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:func-transform2="f:func-transform2"
exclude-result-prefixes="xsl func-transform2"
>
   <xsl:import href="transform-and-sum.xsl"/>
   <xsl:import href="hex-to-decimal.xsl"/>

   <xsl:output method="text"/>
   
   <func-transform2:func-transform2/>

    <xsl:template match="/">
      <xsl:call-template name="transform-and-sum">
        <xsl:with-param name="pFuncTransform" 
                        select="document('')/*/func-transform2:*[1]"/>
        <xsl:with-param name="pList" select="/*/*"/>
      </xsl:call-template>
    </xsl:template>
    
    <xsl:template match="func-transform2:*">
      <xsl:param name="arg" select="0"/>
      
      <xsl:call-template name="hex-to-decimal">
        <xsl:with-param name="pxNumber" select="$arg"/>
      </xsl:call-template>
    </xsl:template>

</xsl:stylesheet>

This, when applied to the following xml source doc:

<t>
  <Amount>4000</Amount>
  <Amount>1000</Amount>
</t>

Gives the following result:
20480

Now, here's the code of the more interesting functions "transform-and-sum" and
"hex-to-decimal":

transform-and-sum.xsl:
---------------------
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:sum-fold-func="f:sum-fold-func"
exclude-result-prefixes="xsl sum-fold-func"
>
   <xsl:import href="foldl.xsl"/>

   <sum-fold-func:sum-fold-func/>

    <xsl:template name="transform-and-sum">
      <xsl:param name="pFuncTransform" select="/.."/>
      <xsl:param name="pList" select="/.."/>

      <xsl:variable name="vrtfFoldFun">
        <sum-fold-func:sum-fold-func/>
        <xsl:copy-of select="$pFuncTransform"/>
      </xsl:variable>
      
      <xsl:call-template name="foldl">
        <xsl:with-param name="pFunc" select="msxsl:node-set($vrtfFoldFun)/*"/>
        <xsl:with-param name="pList" select="$pList"/>
        <xsl:with-param name="pA0" select="0"/>
      </xsl:call-template>
    </xsl:template>

    <xsl:template name="add" match="sum-fold-func:*">
         <xsl:param name="arg0" select="/.."/>
         <xsl:param name="arg1" select="0"/>
         <xsl:param name="arg2" select="0"/>
         
         <xsl:variable name="vPartialCompose">
           <xsl:apply-templates select="$arg0">
             <xsl:with-param name="arg" select="$arg2"/>
           </xsl:apply-templates>
         </xsl:variable>
         
         <xsl:value-of select="$arg1 + $vPartialCompose"/>
    </xsl:template>

</xsl:stylesheet>

hex-to-decimal.xsl:
------------------
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:hex-converter="f:hex-converter"
>
  <xsl:import href="str-foldl.xsl"/>
  
  <hex-converter:hex-converter/>
  
  <xsl:variable name="hexDigits" select="'0123456789ABCDEF'"/>
  
  <xsl:template name="hex-to-decimal">
    <xsl:param name="pxNumber"/>
    
    <xsl:variable name="vFunXConvert" 
                  select="document('')/*/hex-converter:*[1]"/>
    
    <xsl:call-template name="str-foldl">
      <xsl:with-param name="pFunc" select="$vFunXConvert"/>
      <xsl:with-param name="pA0" select="0"/>
      <xsl:with-param name="pStr" select="$pxNumber"/>
    </xsl:call-template>
  </xsl:template>
  
  <xsl:template match="hex-converter:*">
    <xsl:param name="arg1"/> <!-- $pA0 -->
    <xsl:param name="arg2"/> <!-- a char (digit) -->
    
    <xsl:value-of select="16 * $arg1 
                        + string-length(substring-before($hexDigits, $arg2))"/>
  </xsl:template>
</xsl:stylesheet>

As usual, these functions use either "foldl" or "str-foldl" to perform any necessary
generic list-folding operations (respectively on a list of nodes, or on a string
(list of characters)).

The XSLT implementations of "foldl" and "str-foldl" have been already provided at:

http://sources.redhat.com/ml/xsl-list/2001-11/msg00214.html

and

http://sources.redhat.com/ml/xsl-list/2001-11/msg00488.html



Cheers,

Dimitre Novatchev.

__________________________________________________
Do You Yahoo!?
Find the one for you at Yahoo! Personals
http://personals.yahoo.com

 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list


Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.