Subject: Re: [xsl] Variable scope issue. From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Sat, 9 Feb 2002 13:24:09 +0000 |
Hi Nitin, > The XML data is presorted.( wrt currency type and date ) This is a grouping problem that looks on the surface very similar to the one that I just replied to about grouping airline elements, but needs a more complex solution because of the requirement to provide summary statistics about each group. You want to group your reports elements by two things: Currency and ChequeDate. You want to provide a total for all the reports with the same Currency, and a subtotal for all the reports with the same date within each Currency group. The problem is, given that you're on a particular reports element, how to get hold of all the reports in your list that have the same Currency. You can do it by a simple XPath: ../reports[Currency = current()/Currency] but this is fairly inefficient, especially because you'll be doing it lots of times over the course of the transformation. It's better to use a key to index all the reports elements by their Currency, as follows: <xsl:key name="reports-by-currency" match="reports" use="Currency" /> That enables you to collect together all the reports that have the same Currency as the current reports element with: key('reports-by-currency', Currency) Which is much more efficient. This enables you to create your total, using the sum() function, with: Total: <xsl:value-of select="sum(key('reports-by-currency', Currency) /DepositAmt)" /> The Subtotals are a little more complicated, because you need to group the reports elements by *both* Currency and ChequeDate. But you can approach it in the same way - create a key that indexes the reports elements by a combination of Currency and ChequeDate, as follows: <xsl:key name="reports-by-currency-and-date" match="reports" use="concat(Currency, '+', ChequeDate)" /> And create the subtotal with: Subtotal: <xsl:value-of select="sum(key('reports-by-currency-and-date', concat(Currency, '+', ChequeDate)) /DepositAmt)" /> To collect together the groups of reports themselves, I'd use the same method as I recommended for grouping the airlines in the previous mail. Apply templates to all the reports elements. Within the template, check the immediately preceding sibling to see whether you need to see whether you need to add a Date/Amount header and a Currency header. Also check the immediately following sibling to see whether you need to add a SubTotal footer and a Total footer, as follows: <xsl:template match="reports"> <xsl:choose> <xsl:when test="preceding-sibling::reports[1]/Currency != Currency"> Currency: <xsl:value-of select="Currency" /> <xsl:text>
Date Amount
</xsl:text> </xsl:when> <xsl:when test="preceding-sibling::reports[1]/ChequeDate != ChequeDate"> <xsl:text>
Date Amount
</xsl:text> </xsl:when> </xsl:choose> <xsl:value-of select="ChequeDate" /> <xsl:text> </xsl:text> <xsl:value-of select="DepositAmt" /> <xsl:text>
</xsl:text> <xsl:choose> <xsl:when test="following-sibling::reports[1]/Currency != Currency"> <xsl:text>Subtotal </xsl:text> <xsl:value-of select="sum(key('reports-by-currency-and-date', concat(Currency, '+', ChequeDate)) /DepositAmt)" /> <xsl:text>
Total </xsl:text> <xsl:value-of select="sum(key('reports-by-currency', Currency) /DepositAmt)" /> <xsl:text>

</xsl:text> </xsl:when> <xsl:when test="following-sibling::reports[1]/ChequeDate != ChequeDate"> <xsl:text>Subtotal </xsl:text> <xsl:value-of select="sum(key('reports-by-currency-and-date', concat(Currency, '+', ChequeDate)) /DepositAmt)" /> <xsl:text>
</xsl:text> </xsl:when> </xsl:choose> </xsl:template> It might be neater to split this up into separate templates, but hopefully you get the idea. --- Under the current XSLT 2.0 Working Draft, you could do this with: <xsl:for-each-group select="reports" group-by="Currency"> <xsl:text>Currency: </xsl:text> <xsl:value-of select="Currency" /> <xsl:for-each-group select="current-group()" group-by="ChequeDate"> <xsl:text>
Date Amount
</xsl:text> <xsl:for-each select="current-group()"> <xsl:value-of select="ChequeDate" /> <xsl:text> </xsl:text> <xsl:value-of select="DepositAmt" /> <xsl:text>
</xsl:text> </xsl:for-each> <xsl:text>SubTotal </xsl:text> <xsl:value-of select="sum(current-group()/DepositAmt)" /> <xsl:text>
</xsl:text> </xsl:for-each-group> <xsl:text>Total </xsl:text> <xsl:value-of select="sum(current-group()/DepositAmt)" /> <xsl:text>

</xsl:text> </xsl:for-each-group> (Again, since the reports are in order, you could use group-adjacent rather than group-by. I'm not certain whether there would be any advantages in doing so.) This is a nice illustration of the flexibility of the current-group() function - using it to provide the items in the group both for creating further subgroups or item details, and for providing summary statistics for the group. Cheers, Jeni --- Jeni Tennison http://www.jenitennison.com/ XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] Variable scope issue., Wendell Piez | Thread | Re: [xsl] Variable scope issue., Nitin . Jain |
Re: [xsl] How to get a "heading" fr, Jeni Tennison | Date | RE: [xsl] SaxonServlet, Joerg Pietschmann |
Month |