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: multiple sorting


Jeni,

They work beautifully!  Thank you :-)

Yuko

-----Original Message-----
From: Jeni Tennison [mailto:mail@jenitennison.com]
Sent: Tuesday, August 15, 2000 1:31 PM
To: Caras, Yuko
Cc: 'XSL-List@mulberrytech.com'
Subject: Re: multiple sorting


Yuko,

I can see what you're trying to do here.  Basically, if the name of the
element to be sorted on is 'name' or 'lead', then you want to sort
alphabetically in ascending order, whereas if it's 'score' or 'time', then
you want to sort numerically in descending order.

The trouble is that you can't conditionally add sorts within a xsl:for-each
(or xsl:apply-templates).  There are two ways that I can see of going about
it:

METHOD 1
--------

You could have two versions of the xsl:for-each that iterates over the
rows: one that sorted alphabetically in ascending order, and another that
sorted them numerically in descending order, with the $item determining
which of the xsl:for-eaches was used.  In this case, it's probably best to
separate off your row-matching template and use xsl:apply-templates rather
than xsl:for-each, to prevent having to repeat yourself unnecessarily:

  <xsl:choose>
    <xsl:when test="$item = 'name' or $item = 'lead'">
      <xsl:apply-templates select="row">
        <xsl:sort select="*[name() = $item]"
                  data-type="text" order="ascending" />
      </xsl:apply-templates>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="row">
        <xsl:sort select="*[name() = $item]" 
                  data-type="number" order="descending" />
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>

...

<xsl:template match="row">
  <tr>
    <xsl:for-each select="name">
      <td><xsl:value-of select="."/></td>
    </xsl:for-each>
    <xsl:for-each select="score">
      <td><xsl:value-of select="."/></td>
    </xsl:for-each>
    <xsl:for-each select="time">
      <td><xsl:value-of select="."/></td>
    </xsl:for-each>
    <xsl:for-each select="lead">
      <td><xsl:value-of select="."/></td>
    </xsl:for-each>	  
  </tr>
</xsl:template>

[Actually, this should probably be:

<xsl:template match="row">
  <tr>
    <td><xsl:value-of select="name"/></td>
    <td><xsl:value-of select="score"/></td>
    <td><xsl:value-of select="time"/></td>
    <td><xsl:value-of select="lead"/></td>
  </tr>
</xsl:template>

as long as there's only one name, score, time and lead per row.]

METHOD 2
--------

You could do something clever with the sort so that a particular xsl:sort
instruction would only actually made a difference if a certain case was
true.  For example, if you did:

<xsl:for-each select="row">
  <xsl:sort select="(name | lead)[name() = $item]"
            data-type="text" order="ascending" />
  <xsl:sort select="(score | time)[name() = $item]"
            data-type="number" order="descending" />
  <tr>
    <td><xsl:value-of select="name"/></td>
    <td><xsl:value-of select="score"/></td>
    <td><xsl:value-of select="time"/></td>
    <td><xsl:value-of select="lead"/></td>
  </tr>
</xsl:for-each>

then the select would only be able to find a node to use to sort with for
the first xsl:sort if $item was either 'name' or 'lead'.  Without such a
value, it would have nothing to sort on, and the instruction would have no
effect.  Similarly, the second xsl:sort would only get a sort value if
$item were either 'score' or 'time'.

I think this is a pretty neat way of doing it :)  I've tested it and it
works in SAXON.

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]