This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
RE: Trouble with grouping
- From: "David B. Bitton" <david at codenoevil dot com>
- To: "'Jeni Tennison'" <jeni at jenitennison dot com>
- Cc: xsl-list at lists dot mulberrytech dot com
- Date: Wed, 06 Feb 2002 09:38:07 -0500
- Subject: RE: [xsl] Trouble with grouping
- Organization: Code No Evil, LLC
- Reply-to: xsl-list at lists dot mulberrytech dot com
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@jenitennison.com]
> Sent: Wednesday, February 06, 2002 5:23 AM
> To: David B. Bitton
> Cc: xsl-list@lists.mulberrytech.com
> 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