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]
Other format: [Raw text]

Re: How to get a "heading" from an element


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


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