Subject: Re: [xsl] Conditional display of subtotal element From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx> Date: Wed, 17 Apr 2002 10:12:23 +0100 |
Hi Steve, > I've combined info from an archived thread to generate subtotals and > totals (thanks, Jeni). My problem now is that there's a requirement > for only the first date heading for a subtotal to be shown. The > first report listing is what I'd like, the second is what I'm > currently getting. Following the report examples are the xml file > and the xsl. I'm looking for suggestions for how to show or not show > the date based on if it's the first one for this grouping. Hmm... the grouping method that you're using isn't particularly suited to this kind of task. What you're doing is using a "flat" grouping method, where you just process all the rows at the same time, and then work out for each row whether it's at the beginning or end of a particular group in order to work out what to display. What I'd recommend is that you use a "hierarchical" grouping method, where you process a representative of the group to get group-level output, and then the instances of the group to get item-level output. The grouping constructs in XSLT 2.0 make this approach easier to understand, so I'll just show you that before showing you the XSLT 1.1 method. You want to group your rows by RESET_DATE: <xsl:for-each-group select="row" group-by="RESET_DATE"> ... </xsl:for-each-group> And for each of those groups, you need to show the items, grouped by INDEX, followed by the total for that group (which I'll pretend you can do through a separate template called 'total' rather than repeating all the code): <xsl:for-each-group select="row" group-by="RESET_DATE"> <!-- rows --> <xsl:for-each-group select="current-group()" group-by="INDEX"> ... </xsl:for-each-group> <!-- total line --> <xsl:call-template name="total"> <xsl:with-param name="value" select="sum(current-group()/NTL)" /> </xsl:call-template> </xsl:for-each-group> For the groups of rows grouped by INDEX, you need to create a row that contains the date (but only if they're the first of the groups). Again I'll assume that you can do this through a named template rather than repeating lots of code: <xsl:for-each-group select="row" group-by="RESET_DATE"> <!-- rows --> <xsl:for-each-group select="current-group()" group-by="INDEX"> <xsl:call-template name="row"> <xsl:with-param name="date"> <xsl:if test="position() = 1"> <xsl:value-of select="RESET_DATE" /> </xsl:if> </xsl:with-param> <xsl:with-param name="index" select="INDEX" /> <xsl:with-param name="value" select="sum(current-group()/NTL)" /> </xsl:call-template> </xsl:for-each-group> <!-- total line --> <xsl:call-template name="total"> <xsl:with-param name="value" select="sum(current-group()/NTL)" /> </xsl:call-template> </xsl:for-each-group> That's the XSLT 2.0 way, now a XSLT 1.0 way. Usually I'd use the Muenchian Method here (see http://www.jenitennison.com/xslt/grouping) but your input is already sorted in order, which makes things slightly easier. The first task is to locate a representative row for each group of rows, grouped by RESET_DATE. Picking the first of the rows in each group is easy -- you just have to test whether the row has an immediately preceding sibling whose RESET_DATE is the same as this one -- if it hasn't, then you're on the first of the group: <xsl:for-each select="row[not(preceding-sibling::row[1]/RESET_DATE = RESET_DATE)]"> ... </xsl:for-each> You can get the "current group" using the key that you've set up already, 'rows-by-rdate': <xsl:for-each select="row[not(preceding-sibling::row[1]/RESET_DATE = RESET_DATE)]"> <xsl:variable name="current-date-group" select="key('rows-by-rdate', RESET_DATE)" /> ... </xsl:for-each> and then use it to create the total row, and to group by INDEX -- when grouping by INDEX you need to find those rows in the $current-date-group that don't have a preceding sibling row whose INDEX is the same as their own, and the rest of the template follows similarly: <xsl:for-each select="row[not(preceding-sibling::row[1]/RESET_DATE = RESET_DATE)]"> <xsl:variable name="current-date-group" select="key('rows-by-rdate', RESET_DATE)" /> <!-- rows --> <xsl:for-each select="$current-date-group [not(preceding-sibling::row[1]/INDEX = INDEX)]"> <xsl:variable name="current-index-group" select="key('rows-by-rdate-and-index', concat(RESET_DATE, '+', INDEX))" /> <xsl:call-template name="row"> <xsl:with-param name="date"> <xsl:if test="position() = 1"> <xsl:value-of select="RESET_DATE" /> </xsl:if> </xsl:with-param> <xsl:with-param name="index" select="INDEX" /> <xsl:with-param name="value" select="sum($current-index-group/NTL)" /> </xsl:call-template> </xsl:for-each-group> <!-- total line --> <xsl:call-template name="total"> <xsl:with-param name="value" select="sum($current-date-group/NTL)" /> </xsl:call-template> </xsl:for-each> As you can see, if you use this method, you can test whether or not to provide the RESET_DATE by whether the row is the first of the groups grouped by INDEX, simply using position() = 1. 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 -> |
---|---|---|
[xsl] Conditional display of subtot, FRIEDLAND, STEPHEN R | Thread | [xsl] applying same XSL to differen, Najmi, Jamal |
RE: [xsl] sum for a specific attrib, Michael Kay | Date | Re: [xsl] translation string to ele, cutlass |
Month |