RE: [xsl] Trouble with grouping

Subject: RE: [xsl] Trouble with grouping
From: "David B. Bitton" <david@xxxxxxxxxxxxxx>
Date: Wed, 06 Feb 2002 09:38:07 -0500
I can not say thank you enough.  If you are ever in NYC, lemme know, I
owe you a beer.  For now, I'll just call you Dame Jeni Tennison, ok?  :)

> -----Original Message-----
> From: Jeni Tennison [mailto:jeni@xxxxxxxxxxxxxxxx] 
> Sent: Wednesday, February 06, 2002 5:23 AM
> To: David B. Bitton
> Cc: xsl-list@xxxxxxxxxxxxxxxxxxxxxx
> Subject: Re: [xsl] Trouble with grouping
> 
> 
> Hi David,
> 
> > I have found salvation within this list in regards to grouping and 
> > sorting before. I'm at my wits ends with this one. For some 
> reason, I 
> > am displaying records outside of the current context, and I cannot 
> > figure out why. Oh, and the additional records coming 
> through are not 
> > being displayed where their context is correct. The XSL and 
> XML files 
> > are a little to long to attach, so they are here:
> 
> This is one of the classic problems with Muenchian grouping...
> 
> Keys always index all the elements that match the pattern in 
> their match attribute *throughout the document*. So when you do:
> 
> <xsl:key name="distinct" match="Transaction" use="IsCredit" />
> 
> You index *all* the Transaction elements in the document, 
> whatever Account they are in, based on their IsCredit value.
> 
> When you try to get the unique ones within a particular 
> Account, with (in the context of an Account element):
> 
>   Transactions/Transaction[count(. | key('distinct', 
> IsCredit)[1]) = 1]
> 
> then what you're actually doing is searching through the 
> Transaction elements within the Account, and locating those 
> that are first *in the entire document, across Accounts* with 
> a particular IsCredit value. That's why you're sometimes 
> *not* getting Transactions that you know that you should - 
> unless the Transactions within this Account happen to be the 
> first in the document with that particular IsCredit value 
> (which would generally only be for the first Account, I 
> imagine), you won't see them.
> 
> Also, when you do:
> 
>   key('distinct', IsCredit)
> 
> to get all the Transactions with the same IsCredit value, you get
> *all* the Transactions, across the entire document, across 
> Accounts, with that particular IsCredit value. Which is why 
> you're getting Transactions in Accounts when you didn't want them.
> 
> So that's the problem. One solution is to make the key 
> include some information about the identity of the Account in 
> which the Transaction's occurred, as well as the IsCredit 
> value. You need some unique identifier for each Account - I'm 
> guessing that the Account's Statement/Number acts as a unique 
> identifier; if all else fails you can use generate-id() to create one.
> 
> Change the key to include the Account's identifier within the 
> key value, like so:
> 
> <xsl:key name="distinct" match="Transaction"
>   use="concat(ancestor::Account/Statement/Number, '+', IsCredit)" />
> 
> Then to get the unique ones within the current Account 
> element, you can do:
> 
>   <xsl:variable name="account" select="Statement/Number" />
>   <xsl:apply-templates
>     select="Transactions/Transaction
>               [count(. | key('distinct',
>                              concat($account, '+', 
> IsCredit))[1]) = 1]">
>     <xsl:sort select="IsCredit" order="descending" />
>   </xsl:apply-templates>
> 
> And to get all the rest of the Transactions with that 
> particular IsCredit within the Account of the current 
> Transaction, you can use:
> 
>   key('distinct',
>       concat(ancestor::Account/Statement/Number, '+', IsCredit))
> 
> ---
> 
> In XSLT 2.0, because you don't use keys, these kinds of 
> scoping problems don't occur. You can simply do:
> 
>   <xsl:for-each-group select="Transactions/Transaction"
>                       group-by="IsCredit">
>     <xsl:sort select="IsCredit" order="descending" />
>     ...
>     <xsl:for-each select="current-group()">
>       ...
>     </xsl:for-each>
>     ...
>   </xsl:for-each-group>
> 
> Does anyone else feel that an apply-templates-group 
> equivalent would be useful? I have a feeling that a lot of 
> people use a template, rather than a xsl:for-each, when 
> generating group-level output at the moment (certainly that's 
> what I've standardly done), which means changing things 
> around quite a lot when you start using XSLT 2.0 grouping 
> methods instead.
> 
> Of course you can split things up by doing:
> 
>   <xsl:for-each-group select="Transactions/Transaction"
>                       group-by="IsCredit">
>     <xsl:sort select="IsCredit" order="descending" />
>     <xsl:apply-templates select="." />
>   </xsl:for-each-group>
> 
> ...
> 
> <xsl:template match="Transaction">
>     ...
>     <xsl:for-each select="current-group()">
>       ...
>     </xsl:for-each>
>     ...
> </xsl:template>
> 
> But the position() within the Transaction template is then 
> always 1, whereas with something like:
> 
>   <xsl:apply-templates-group select="Transactions/Transaction"
>                              group-by="IsCredit">
>     <xsl:sort select="IsCredit" order="descending" />
>   </xsl:apply-templates-group>
> 
> then the position() changes as you would expect.
> 
> Cheers,
> 
> Jeni
> 
> ---
> Jeni Tennison
> http://www.jenitennison.com/
> 


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


Current Thread