This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: RE: grouping content
- To: Gavin Myers <GavinM at yatra dot net>
- Subject: Re: [xsl] RE: grouping content
- From: Jeni Tennison <mail at jenitennison dot com>
- Date: Fri, 6 Apr 2001 20:38:07 +0100
- CC: "'XSL-List at lists dot mulberrytech dot com'" <XSL-List at lists dot mulberrytech dot com>
- Organization: Jeni Tennison Consulting Ltd
- References: <D59A7D94E5FED4118CD300B0D068723C04F07A@spice.yatracorp.com>
- Reply-To: xsl-list at lists dot mulberrytech dot com
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