This is the mail archive of the
xsl-list@mulberrytech.com
mailing list .
Re: How to get a "heading" from an element
- From: Jeni Tennison <jeni at jenitennison dot com>
- To: Roger Chung-Wee <roger at chung-wee dot com>
- Cc: xsl-list at lists dot mulberrytech dot com
- Date: Sat, 9 Feb 2002 12:52:40 +0000
- Subject: Re: [xsl] How to get a "heading" from an element
- Organization: Jeni Tennison Consulting Ltd
- References: <l6n56ukh2cc5nlv5sl2535hokdc1ibho5m@4ax.com>
- Reply-to: xsl-list at lists dot mulberrytech dot com
Hi Roger,
> I'm having a real problem with something apparently simple.
This is a grouping problem. You want to group the airline elements in
your source document into groups according to their airline-name.
>From the looks of it, the airline elements are listed in order, and
the kind of grouping that you want to do just involves adding a header
at the top of each group. That means you can use something quite
simple to do the grouping.
Iterate over the airline elements one by one; I'd apply templates to
them as follows:
<xsl:apply-templates select="airline" />
Then have a template that matches airline elements:
<xsl:template match="airline">
...
</xsl:template>
But of course you could use an xsl:for-each instead if you're more
comfortable with it.
For each airline element, you want to create a row that contains a
cell for the airport-code and a cell for the airport-name, as follows:
<tr>
<td><xsl:value-of select="airport-code" /></td>
<td><xsl:value-of select="airport-name" /></td>
</tr>
Then for certain airline elements, you want to add a header before
this row. The airline elements where you need to add a header are
those that are the first with a particular airline-name. Given that
your file is sorted, you can tell which are first with a particular
airline-name by looking at their immediately preceding sibling airline
(if they have one). If the airline-name of the immediately preceding
sibling airline is different from the airline-name of this airline,
then it's the first in a group.
You can get the immediately preceding sibling airline with:
preceding-sibling::airline[1]
You can compare its airline-name with this airline's airline-name
with:
preceding-sibling::airline[1]/airline-name != airline-name
And you can use an xsl:if to decide, on the basis of this test,
whether to add the header or not. So the content of the xsl:template
or xsl:for-each would be:
<xsl:if test="preceding-sibling::airline[1]/airline-name !=
airline-name">
<tr>
<td><xsl:value-of select="airline-name" /></td>
</tr>
</xsl:if>
<tr>
<td><xsl:value-of select="airport-code" /></td>
<td><xsl:value-of select="airport-name" /></td>
</tr>
Note that this only works because your airline elements are already
sorted by airline-name. If they were not sorted, you'd need to use a
more sophisticated grouping method, such as Muenchian grouping (see
http://www.jenitennison.com/xslt/grouping/muenchian.html).
---
For interest, the XSLT 2.0 method would be:
<xsl:for-each-group select="airline" group-by="airline-name">
<tr>
<td><xsl:value-of select="airline-name" /></td>
</tr>
<xsl:for-each select="current-group()">
<tr>
<td><xsl:value-of select="airport-code" /></td>
<td><xsl:value-of select="airport-name" /></td>
</tr>
</xsl:for-each>
</xsl:for-each-group>
Or, alternatively, since you're dealing with an ordered list of
airlines:
<xsl:for-each-group select="airline" group-adjacent="airline-name">
<tr>
<td><xsl:value-of select="airline-name" /></td>
</tr>
<xsl:for-each select="current-group()">
<tr>
<td><xsl:value-of select="airport-code" /></td>
<td><xsl:value-of select="airport-name" /></td>
</tr>
</xsl:for-each>
</xsl:for-each-group>
I hope that helps,
Jeni
---
Jeni Tennison
http://www.jenitennison.com/
XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list