[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message]

Re: 2 level Grouping through attributes

Subject: Re: 2 level Grouping through attributes
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Mon, 11 Feb 2002 19:09:42 +0000
variables xsl grouping attributes
Ronald Heller wrote:
> Try the following:
> <?xml version='1.0'?>
> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
> <xsl:output method="xml" indent="yes"/>
>     <xsl:key name="level1" match="XML_OUTPUT/@UserID" use="." />
>
>     <xsl:key name="level2" match="XML_OUTPUT/@CategoryID" use="." />
>
>     <xsl:template match="/">
>        <ROOT>
>           <xsl:for-each select="//XML_OUTPUT[generate-id(@UserID) = 
> generate-id(key('level1', @UserID)[1])]">
>              <xsl:variable name="user">
>                 <xsl:value-of select="@UserID" />
>              </xsl:variable>
>
>              <xsl:comment>
>                 <xsl:value-of select="generate-id(@UserID)" />
>              </xsl:comment>
>
>              <USER ID="{$user}">
>                 <xsl:for-each select="//XML_OUTPUT[generate-id(@CategoryID) 
> = generate-id(key('level2', @CategoryID)[1]) and @UserID=$user]">
>                    <xsl:variable name="category">
>                       <xsl:value-of select="@CategoryID" />
>                    </xsl:variable>
>
>                    <CATEGORY ID="{$category}" TITLE="{@CategoryTitle}">
>                       <xsl:for-each 
> select="//XML_OUTPUT[@CategoryID=$category and @UserID=$user]">
>                          <CONTENT_ITEM ID="{@ContentItemID}" 
> TITLE="{@ItemTitle}">
>                          </CONTENT_ITEM>
>                       </xsl:for-each>
>                    </CATEGORY>
>                 </xsl:for-each>
>              </USER>
>           </xsl:for-each>
>        </ROOT>
>     </xsl:template>
> </xsl:stylesheet>

Unfortunately this runs into the classic trap with multi-level
grouping using the Muenchian Method when identifying the unique values
at the second level. See:

> <xsl:for-each select="//XML_OUTPUT[generate-id(@CategoryID)
> = generate-id(key('level2', @CategoryID)[1]) and @UserID=$user]">

Here, you go through all the XML_OUTPUT elements in the document, and
then identify those that:

  (a) are the first in the document with that particular CategoryID;
  and
  (b) have the UserID equal to $user

This will miss out XML_OUTPUT elements that have the same user ID, but
*aren't* the first in the document with the particular category ID. So
you won't pick up everything in the document.

To do two-level grouping using the Muenchian method, the key for the
second level has to incorporate information from the first level. I'd
use the keys:

<xsl:key name="level1" match="XML_OUTPUT" use="@UserID" />
<xsl:key name="level2" match="XML_OUTPUT"
         use="concat(@UserID, '+', @CategoryID)" />

This has two advantages: it gives you the right answer, and it means
you can use the 'level2' key to find all the XML_OUTPUT elements with
a particular user and category, which is a lot more efficient than
searching through the entire document again.
         
Then:

<xsl:template match="/">
  <ROOT>
    <xsl:for-each
      select="//XML_OUTPUT[generate-id() =
                           generate-id(key('level1', @UserID)[1])]">
      <xsl:variable name="user" select="@UserID" />

      <xsl:comment>
        <xsl:value-of select="generate-id(@UserID)" />
      </xsl:comment>

      <USER ID="{$user}">
        <xsl:for-each
          select="key('level1', $user)
                    [generate-id() =
                     generate-id(key('level2',
                                     concat($user, '+', @CategoryID)[1])]">
          <xsl:variable name="category" select="@CategoryID" />

          <CATEGORY ID="{$category}" TITLE="{@CategoryTitle}">
            <xsl:for-each select="key('level2',
                                      concat($user, '+', $category)">
              <CONTENT_ITEM ID="{@ContentItemID}" TITLE="{@ItemTitle}">
              </CONTENT_ITEM>
            </xsl:for-each>
          </CATEGORY>
        </xsl:for-each>
      </USER>
    </xsl:for-each>
  </ROOT>
</xsl:template>

---

The XSLT 2.0 solution is very similar:

<xsl:template match="/">
  <ROOT>
    <xsl:for-each-group select="//XML_OUTPUT" group-by="@UserID">
      <xsl:variable name="user" select="@UserID" />

      <xsl:comment>
        <xsl:value-of select="generate-id(@UserID)" />
      </xsl:comment>

      <USER ID="{$user}">
        <xsl:for-each-group select="current-group()"
                            group-by="@CategoryID">
          <xsl:variable name="category" select="@CategoryID" />

          <CATEGORY ID="{$category}" TITLE="{@CategoryTitle}">
            <xsl:for-each select="current-group()">
              <CONTENT_ITEM ID="{@ContentItemID}" TITLE="{@ItemTitle}">
              </CONTENT_ITEM>
            </xsl:for-each>
          </CATEGORY>
        </xsl:for-each>
      </USER>
    </xsl:for-each>
  </ROOT>
</xsl:template>

Cheers,

Jeni

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


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


Current Thread

PURCHASE STYLUS STUDIO ONLINE TODAY!

Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced!

Buy Stylus Studio Now

Download The World's Best XML IDE!

Accelerate XML development with our award-winning XML IDE - Download a free trial today!

Don't miss another message! Subscribe to this list today.
Email
First Name
Last Name
Company
Subscribe in XML format
RSS 2.0
Atom 0.3
Site Map | Privacy Policy | Terms of Use | Trademarks
Free Stylus Studio XML Training:
W3C Member
Stylus Studio® and DataDirect XQuery ™are products from DataDirect Technologies, is a registered trademark of Progress Software Corporation, in the U.S. and other countries. © 2004-2013 All Rights Reserved.