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: arbitrary sorting (Part III, long))



Oliver Becker's original question:
>supposing I have elements with a month attribute
  <report month="Jan" />
  <report month="Feb" />
>and so on.
>Of course unordered :-)
>Now I want them in chronological order....
>I know how to translate Jan->1, Feb->2 etc via a named template
>[and] xsl:choose, but that doesn't help much in this case.

Naturally, what you want is to map names to numbers using keys,
which can be very efficient. Keys were made for just this
purpose! So far, I've been able to get this to work if the
month table is in the input document.

Consider this input document:
<?xml version="1.0"?>
<doc>
<monthtab>
  <entry><name>Jan</name><number>1</number></entry>
  <entry><name>January</name><number>1</number></entry>
  <entry><name>Feb</name><number>2</number></entry>
  <entry><name>February</name><number>2</number></entry>
  <entry><name>Mar</name><number>3</number></entry>
  <entry><name>March</name><number>3</number></entry>
  <entry><name>Apr</name><number>4</number></entry>
  <entry><name>April</name><number>4</number></entry>
  <entry><name>May</name><number>5</number></entry>
  <entry><name>Jun</name><number>6</number></entry>
  <entry><name>June</name><number>6</number></entry>
  <entry><name>Jul</name><number>7</number></entry>
  <entry><name>July</name><number>7</number></entry>
  <entry><name>Aug</name><number>8</number></entry>
  <entry><name>August</name><number>8</number></entry>
  <entry><name>Sep</name><number>9</number></entry>
  <entry><name>Sept</name><number>9</number></entry>
  <entry><name>September</name><number>9</number></entry>
  <entry><name>Oct</name><number>10</number></entry>
  <entry><name>October</name><number>10</number></entry>
  <entry><name>Nov</name><number>11</number></entry>
  <entry><name>November</name><number>11</number></entry>
  <entry><name>Dec</name><number>12</number></entry>
  <entry><name>December</name><number>12</number></entry>
</monthtab>
<bday person="Linda"><month>Apr</month><day>22</day></bday>
<bday person="Marie"><month>September</month><day>9</day></bday>
<bday person="Lisa"><month>March</month><day>31</day></bday>
<bday person="Harry"><month>Sep</month><day>16</day></bday>
<bday person="Ginny"><month>Jan</month><day>22</day></bday>
<bday person="Pedro"><month>November</month><day>2</day></bday>
<bday person="Bill"><month>Apr</month><day>4</day></bday>
<bday person="Frida"><month>July</month><day>5</day></bday>
</doc>

The first part of the above document is the month table.
For demonstration purposes, I have both abbreviated and full
month names (look at September) as synonyms, and you could
easily add names in other languages. There's a many-to-one
structure: look up a name, get back the correct month
number. The rest of the document is the set of records that
we want to sort in chronological order. The <day> elements
will work as a simple numeric sort, but that's secondary to
the sort by months. Following Oliver's request, we want
<xsl:sort select="key('MonthNum',month)" data-type="number"/>
where we will take the <month> as a string and get its
number out of the 'MonthNum' keyspace.

I'll supply an example of the above sort working in an
apply-templates situation, but it can work similarly in a
for-each loop. The current node will be the outer <doc>
element at the time we sort, so the keyspace definition will
also be based on that context:
<xsl:key name="MonthNum"
   match="monthtab/entry/number" use="../name" />
With that background, check out this stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0">

<xsl:key name="MonthNum" match="monthtab/entry/number"
  use="../name" />

<xsl:template match="doc">
  <out>
    <xsl:text>Birthdays in chronological order...
</xsl:text>
    <xsl:apply-templates select="bday">
      <xsl:sort select="key('MonthNum',month)"
        data-type="number" />
      <xsl:sort select="day" data-type="number" />
    </xsl:apply-templates>
  </out>
</xsl:template>

<xsl:template match="bday">
  <xsl:value-of select="@person"/><xsl:text>: </xsl:text>
  <xsl:value-of select="month"/><xsl:text> </xsl:text>
  <xsl:value-of select="day"/><xsl:text>
</xsl:text>
</xsl:template>

</xsl:stylesheet>

The <out> element is just something we commonly use as a
tracing aid. The above works on both Xalan and Saxon.

Ideally, one would want to put the month table in a
completely separate file, so it could be shared among all
stylesheets that needed it. Depending on your situation,
you might prefer to have the month table right in the
stylesheet. Either way, you have to use document(), which
certainly complicates the procedure. The point of this
message is to show that key() can be used in sort keys.
.................David Marston


 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]