[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] params - keys? how to calculate product of parents with multiple children


Subject: Re: [xsl] params - keys? how to calculate product of parents with multiple children
From: Dimitre Novatchev <dnovatchev@xxxxxxxxx>
Date: Thu, 14 Apr 2005 06:33:21 +1000

On 4/14/05, Michael Kay <mike@xxxxxxxxxxxx> wrote:
>
> The first part, dividing the two (so-called) attributes is easy:
>
> RECORDITEM[@sqlsource='oldshares'] div RECORDITEM[@sqlsource='newshares']
>
> The next bit is more difficult. There's no built-in function (like sum())
> that takes the product of a set of numbers, but Dimitre's FXSL library
makes
> it very easy to define one of your own. If he's listening, I'm sure he'll
> tell you how.

In XSLT 2.0 using FXSL one will simply use f:product and the whole
transformation is the result of just a single XPath expression:

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:f="http://fxsl.sf.net/"
 exclude-result-prefixes="f"
>
   <xsl:import href="../f/func-product.xsl"/>

    <xsl:output  encoding="UTF-8" omit-xml-declaration="yes"/>

   <!-- This transformation should be applied to:
        testProduct.xml
     -->

    <xsl:template match="/">
      <xsl:value-of select=
      "f:product(/*/*/*/*/(*[@sqlsource='newshares']
                                  div
                                   *[@sqlsource='oldshares']))"/>
    </xsl:template>
</xsl:stylesheet>


When applied on the OP's source xml it produces the wanted result:

  0.25

Here's the code for the f:product() function:

func-product.xsl:
============
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:f="http://fxsl.sf.net/"
exclude-result-prefixes="f xs"
>
   <xsl:import href="func-foldl.xsl"/>
   <xsl:import href="../f/func-Operators.xsl"/>

    <xsl:function name="f:product">
      <xsl:param name="pList"/>
      <xsl:sequence select="f:foldl(f:mult(), 1, $pList)"/>
    </xsl:function>

</xsl:stylesheet>


If someone loves writing expressions from scratch, they could
substitute f:product() in the first transformation with the expression
from the last transformation and they would still get a single XPath
expression for the solution of the problem:


f:foldl(f:mult(), 1, /*/*/*/*/(*[@sqlsource='newshares']
                                     div
                                        *[@sqlsource='oldshares']))
          )


Cheers,
Dimitre Novatchev


>
> The alternative is to do a recursive scan of the values: write a template
> (or XSLT 2.0 function) that takes a RECORDSECTION as an argument and the
> product-so-far as a second argument. Compute the value above, multiply it
by
> the product-so-far, and call the template with the following-sibling
> RECORDSECTION and the new product as the arguments. Start the thing off by
> calling this template/function with the first RECORDSECTION and the value 1
> as arguments, terminate the recursion when there are no following siblings.
>
> Michael Kay
> http://www.saxonica.com/
>
>
> > -----Original Message-----
> > From: Whitney, Dan (CanWest Interactive)
> > [mailto:DWhitney@xxxxxxxxxxx]
> > Sent: 13 April 2005 13:58
> > To: 'mulberry - xsl'
> > Subject: [xsl] params - keys? how to calculate product of
> > parents with multiple children
> >
> >
> > Hi am having trouble conceptualizing how/if I can calculate
> > the following:
> > I want to take the children of RECORDSECTION and divide
> > attribute value
> > newshares by attribute value oldshares.
> > I then want to multiply the results of each those
> > calculations (there may be
> > 0 - any number of occurrences of RECORDSECTION). So for below
> > it would be:
> > (1/2)*(1/2)= 0.25
> >
> > Any help/examples would be appreciated.
> >
> > Dan Whitney
> >
> > XML
> >
> > <?xml version="1.0" encoding="windows-1252"?>
> > <?xml-stylesheet type="text/xsl" href="test/split_total.xsl"?>
> >
> > <PUBLICATION>
> > <DOCUMENT source="splits" group="surveys" output="body">
> > <RECORD fragment="splits" recid="12129" fragid="13" product="surveys"
> > group="surveys" status="1"><ID idtype="fpid">12129</ID>
> > <RECORDSECTION docfragment="splits">
> > <RECORDITEM sqlsource="oldshares">2</RECORDITEM>
> > <RECORDITEM sqlsource="newshares">1</RECORDITEM>
> > </RECORDSECTION>
> > <RECORDSECTION docfragment="splits">
> > <RECORDITEM sqlsource="oldshares">2</RECORDITEM>
> > <RECORDITEM sqlsource="newshares">1</RECORDITEM>
> > </RECORDSECTION>
> > </RECORD>
> > </DOCUMENT>
> > </PUBLICATION>
> >
> > XSL
> >
> > <xsl:stylesheet
> >     version="1.0"
> >     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
> >
> > <xsl:output
> >     method="html"
> >     encoding="iso-8859-1"
> >     indent="yes"
> >     version="4.0"/>
> >
> >
> > <xsl:template match="PUBLICATION">
> >   <table border="0" cellpadding="2" cellspacing="2"
> > width="410" cols="2" >
> >     <tr>
> >       <td>
> >       <xsl:for-each select="DOCUMENT/RECORD">
> > <!-- below would like to only define param once but have it
> > increment based
> > on position-->
> >         <xsl:variable name="product[position()]"
> > select="RECORDSECTION[1]/RECORDITEM[@sqlsource='newshares'] div
> > RECORDSECTION[1]/RECORDITEM[@sqlsource='oldshares']"/>
> >         <xsl:variable name="product[position()]"
> > select="RECORDSECTION[2]/RECORDITEM[@sqlsource='newshares'] div
> > RECORDSECTION[1]/RECORDITEM[@sqlsource='oldshares']"/>
> > <!-- this was a half conceived attempt
> >         <xsl:for-each select="RECORDSECTION[1]">
> >           <xsl:call-template name="splitfactor">
> >             <xsl:with-param name="factor-subset"
> > select="following-sibling::RECORDSECTION"/>
> >           </xsl:call-template>)
> >         </xsl:for-each>
> > -->
> > <!-- here don't even know how I would write this -->
> >         <xsl:value-of select="$product1 * $product2"/>
> >       </xsl:for-each>
> >       </td>
> >     </tr>
> >   </table>
> > </xsl:template>
> >
> > <!-- half conceived attempt -->
> > <xsl:template name="splitfactor">
> >   <xsl:param name="factor-subset" select="1"/>
> >   <xsl:if test="$factor-subset[1]">
> >     <xsl:call-template name="splitfactor">
> >       <xsl:with-param name="factor-subset"
> > select="$factor-subset[position()
> > &gt; 1]"/>
> >     </xsl:call-template>
> >   </xsl:if>
> > </xsl:template>
> >
> > <xsl:template match="ID"/>
> >
> > </xsl:stylesheet>


Current Thread
Keywords