Re: [xsl] Sort-grouping problem

Subject: Re: [xsl] Sort-grouping problem
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Wed, 17 Apr 2002 10:29:17 +0100
Hi Ragulf,

> I thought my whole problem was sorting the results, but when I try
> to make yet another group (within the group by date), I am faced
> with difficulties (which I hope someone can help me with):
>
> Taking my stylesheet below (not sorting it at the moment), I would
> also want to group by P1 (the XML is at the buttom of the mail).

OK, that's a different problem.

> I thought I could do something like this:
> ..
>    <xsl:for-each select="$unique-dates">
>      <xsl:variable name="unique-p1s" 
> select=".[not(P1=preceding-sibling::News/P1)]/P1"/>
> ..
> but I get the error:
> Expected token 'eof' found '['. 
.-->>[<--not(P1=preceding-sibling::News/P1)]/P1

Yes, you're not allowed to use predicates with abbreviated paths. What
you're doing is equivalent to:

  <xsl:variable name="unique-p1s"
    select="self::node()[not(P1 = preceding-sibling::News/P1)]/P1" />

In other words "select the current node if there aren't previous News
elements with the same value for their P1 element".

I doubt that is what you want to do. I think that you want to select
all the News elements in the document that have the same Date as the
current News element that you're looking at, and then choose from them
those that do not have any preceding sibling News elements with the
same Date and the same P1.

With your current method, this would be the following path:

  /NewsList/List/News
    [Date = current()/Date]
    [not(preceding-sibling::News[Date = current()/Date]/P1 = P1)]
    /P1

Personally, though, I'd use the Muenchian Method here (see
http://www.jenitennison.com/xslt/grouping/muenchian.html for a
detailed explanation). The Muenchian Method is preferable because it
uses keys, so it's a lot faster than searching through all the
preceding siblings of a particular News element several times.

Create a key that indexes the News elements by their Date:

<xsl:key name="byDate" match="News" use="Date" />

And a key that indexes them by Date and by P1:

<xsl:key name="byDateAndP1" match="News" use="concat(Date, '+', P1)" />

Then you can locate all the News elements with the same Date as the
current News element with:

  key('byDate', Date)

Which means that you can locate the first News elements with each
particular date with:

  News[generate-id() = generate-id(key('byDate', Date)[1])]

So you can do:

  <!-- for each of the News elements with a unique Date -->
  <xsl:for-each select="News[generate-id() =
                             generate-id(key('byDate', Date)[1])]">
    ...
    <!-- for each of the News elements with that Date and a unique P1
         -->
    <xsl:for-each
      select="key('byDate', Date)
               [generate-id() =
                generate-id(key('byDateAndP1',
                                concat(Date, '+', P1))[1])]">
      ...
    </xsl:for-each>
    ...
  </xsl:for-each>

---

In XSLT 2.0:

  <xsl:for-each-group select="News" group-by="Date">
    ...
    <xsl:for-each-group select="current-group()" group-by="P1">
      ...
    </xsl:for-each-group>
    ...
  </xsl:for-each-group>
  
Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


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


Current Thread