This is the mail archive of the xsl-list@mulberrytech.com mailing list .


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: RE: grouping content


Hi Gavin,

> Now when i do that, for some reason if i try:
>
> <xsl:template match="World/NorthAmerica/US">
> I cannot just have:
>   <xsl:for-each select="[@Area and
> generate-id(.)=generate-id(key('LocationGroups',@Area))]">
>
> That returns an error

Square brackets in an expression indicate a *predicate*.  A predicate
is a test on a node set - it's used to filter in a subset of the node
set, the ones for which the test returns true.

The expression in the above isn't a legal XPath expression - you
haven't said what node set the predicate should be filtering.  That's
why it returns an error.

> So I tried removing the []'s, that still returns an error. 

XSLT processors always expect you to specify a *node set* in a select
attribute.  If you remove the []s in the above, you're left with a
*boolean* expression.  The XSLT processor can't iterate over true() or
false(), so it returns the error.

I think that you intended to do:

  <xsl:for-each select="Location[@Area and
       generate-id(.)=generate-id(key('LocationGroups',@Area))]">
     ...
  </xsl:for-each>

In other words, iterate over the Location elements that:

  (a) have an 'Area' attribute and
  (b) that are the first returned by the 'LocationGroups' key with
      that Area

In your second problem, assuming you don't know all the Areas and
Types in advance, you need multi-level keys to allow you to
quickly get the Location elements for a particular Area or of a
particular Type in a particular Area:

<xsl:key name="locs-by-area"
         match="Location" use="@Area" />
<xsl:key name="locs-by-area-and-type"
         match="Location" use="concat(@Area, ':', @Type)" />

In the template matching the US, then you can firstly select the
Location elements with unique Areas, and then select those with unique
Areas and Types, and finally select the relevant Rural or Urban
Location:

<xsl:template match="US">
   <!-- iterate over the Location elements with unique Areas -->
   <xsl:for-each select="Location[generate-id() =
                                  generate-id(key('locs-by-area', @Area))]">
      <!-- output a line giving the Area -->
      <xsl:value-of select="@Area" /><br /><br />
      <!-- iterate over the Location elements with this Area, but only
           those with unique Types -->
      <xsl:for-each
            select="key('locations-by-area', @Area)[generate-id() =
                       generate-id(key('locs-by-area-and-type',
                                       concat(@Area, ':', @Type))]">
         <!-- output a line giving the Type -->
         <xsl:value-of select="@Type" /><br />
         <!-- create a variable to hold the Locations with that Type
              in that Area -->
         <xsl:variable name="locations"
                       select="key('locs-by-area-and-type',
                                   concat(@Area, ':', @Type)" />
         <xsl:text>Rural: </xsl:text>
         <!-- give the value of the Location with Rural Scope -->
         <xsl:value-of select="$locations[@Scope = 'Rural']/@value" />
         <br />
         <xsl:text>Urban: </xsl:text>
         <!-- give the value of the Location with Urban Scope -->
         <xsl:value-of select="$locations[@Scope = 'Urban']/@value" />
         <br />
      </xsl:for-each>
   </xsl:for-each>
</xsl:template>

You can do this through applying templates instead, if you prefer that
style:

<xsl:template match="US">
   <!-- iterate over the Location elements with unique Areas -->
   <xsl:apply-templates mode="area-group"
      select="Location[generate-id() =
                       generate-id(key('locs-by-area', @Area))]" />
</xsl:template>

<xsl:template match="Location" mode="area-group">
   <!-- output a line giving the Area -->
   <xsl:value-of select="@Area" /><br /><br />
   <!-- iterate over the Location elements with this Area, but only
        those with unique Types -->
   <xsl:apply-templates mode="area-and-type-group"
      select="key('locations-by-area', @Area)[generate-id() =
                 generate-id(key('locs-by-area-and-type',
                                 concat(@Area, ':', @Type))]">
</xsl:template>

<xsl:template match="Location" mode="area-and-type-group">
   <!-- output a line giving the Type -->
   <xsl:value-of select="@Type" /><br />
   <!-- create a variable to hold the Locations with that Type
        in that Area -->
   <xsl:variable name="locations"
                 select="key('locs-by-area-and-type',
                             concat(@Area, ':', @Type)" />
   <!-- apply templates to the location with Rural scope -->
   <xsl:apply-templates select="$locations[@Scope = 'Rural']" />
   <!-- apply templates to the location with Urban scope -->
   <xsl:apply-templates select="$locations[@Scope = 'Urban']" />
</xsl:template>

<xsl:template match="Location">
   <xsl:value-of select="@Scope" />: <xsl:value-of select="@value" />
   <br />
</xsl:template>

I hope that helps,

Jeni

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



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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]