[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

RE: [xsl] XSLT2: Clustering, or Grouping the groups


Subject: RE: [xsl] XSLT2: Clustering, or Grouping the groups
From: "Michael Kay" <mhk@xxxxxxxxx>
Date: Tue, 25 May 2004 08:16:29 +0100

I think that when you need to do two levels of grouping like this, it is
usually easier to do it top-down: that is, do the outer level first. Doing
it bottom-up as you are attempting also works, but it requires two passes
over the data.

The top-down solution (untested) looks something like this:

<xsl:for-each-group select="bar" group-adjacent="exists(@baz)">
  <xsl:choose>
  <xsl:when test="exists(@baz)">
    <list>
      <xsl:for-each-group select="current-group() group-adjacent="@baz">
         <list-item>
            <xsl:copy-of select="current-group()" />
         </list-item>
      </xsl:for-each-group>
    </list>
  </xsl:when>
  <xsl:otherwise>
      <xsl:copy-of select="current-group()"/>
  </xsl:otherwise>
  </xsl:choose>
</xsl:for-each-group>


> -----Original Message-----
> From: A. Pagaltzis [mailto:pagaltzis@xxxxxx] 
> Sent: 24 May 2004 22:52
> To: XSLT-List
> Subject: [xsl] XSLT2: Clustering, or Grouping the groups
> 
> Hi all,
> 
> I am using XSLT2 as implemented by Saxon 7.9.1 to group a flat
> structure. What I start out with is something like
> 
>     <foo>
>         <bar baz="1" />
>         <bar baz="2" />
>         <bar baz="2" />
>         <bar />
>         <bar baz="1" />
>         <bar baz="1" />
>         <bar />
>     </foo>
> 
> Now I need to put these bars in a list. The first step is easy
> enough (I'm only paraphrasing; excuse any syntax errors please):
> 
>     <xsl:for-each-group select="bar" group-adjacent="@baz">
>         <xsl:choose>
>             <xsl:when test="@baz">
>                 <list-item>
>                     <xsl:copy-of select="current-group()" />
>                 </list-item>
>             </xsl:when>
>             <xsl:otherwise>
>                 <xsl:copy-of select="current-group()" />
>             </xsl:otherwise>
>         </xsl:choose>
>     </xsl:for-each-group>
> 
> Now what I get looks like this:
> 
>     <foo>
>         <list-item>
>             <bar baz="1" />
>         </list-item>
>         <list-item>
>             <bar baz="2" />
>             <bar baz="2" />
>         </list-item>
>         <bar />
>         <list-item>
>             <bar baz="1" />
>             <bar baz="1" />
>         </list-item>
>         <bar />
>     </foo>
> 
> This is all well and good, but now I need to get to
> 
>     <foo>
>         <list>
>             <list-item>
>                 <bar baz="1" />
>             </list-item>
>             <list-item>
>                 <bar baz="2" />
>                 <bar baz="2" />
>             </list-item>
>         </list>
>         <bar />
>         <list>
>             <list-item>
>                 <bar baz="1" />
>                 <bar baz="1" />
>             </list-item>
>         </list>
>         <bar />
>     </foo>
> 
> The only way I've succeeded in doing this was by two separate
> stylesheets (where the second is pretty much a copy of the first,
> except for different conditions and element names), which is
> suboptimal.
> 
> I'd like to do this in a single step. I tried assigning the
> output to a variable and processing it afterwards using something
> like
> 
>     <xsl:variable name="list">
>         <xsl:for-each-group select="bar" group-adjacent="@baz">
>             <xsl:choose>
>                 <xsl:when test="@baz">
>                     <list-item>
>                         <xsl:copy-of select="current-group()" />
>                     </list-item>
>                 </xsl:when>
>                 <xsl:otherwise>
>                     <xsl:copy-of select="current-group()" />
>                 </xsl:otherwise>
>             </xsl:choose>
>         </xsl:for-each-group>
>     </xsl:variable>
>     <xsl:for-each-group select="$list" 

Your mistake here is that $list is a document node at the root of a
temporary tree: it is a sequence of length one, so using it as the grouping
population is not going to do much good. You should either select the
children of $list (which will include <list-item> and <baz> elements), or
you should declare the variable as a sequence of elements, by writing
<xsl:variable name="list" as="element()*">, in which case the document node
will not be constructed.

Michael Kay


> group-adjacent="boolean(self::list-item)">
>         <xsl:choose>
>             <xsl:when test="   SOME-TEST-HERE()   "> <!-- KEY BIT -->
>                 <list-item>
>                     <xsl:copy-of select="current-group()" />
>                 </list-item>
>             </xsl:when>
>             <xsl:otherwise>
>                 <xsl:copy-of select="current-group()" />
>             </xsl:otherwise>
>         </xsl:choose>
>     </xsl:for-each-group>
> 
> Unfortunately, both what the context node is as well as the value
> of current-group() at the point of SOME-TEST-HERE() seem to defy
> all reason or reasonable expectation in such a way that I can't
> figure out how to get the desired result.
> 
> Uh.. help?
> 
> -- 
> Thanks muchly in advance,
> Aristotle
>  
> "If you can't laugh at yourself, you don't take life 
> seriously enough."


Current Thread