[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] forum threads sorting


Subject: Re: [xsl] forum threads sorting
From: Jeni Tennison <jeni@xxxxxxxxxxxxxxxx>
Date: Tue, 26 Feb 2002 17:08:49 +0000

Hi Oleg,

> I can sort by root/aaa dates, but I can't consider descendant
> messages
>
> <xsl:sort select="translate(@vl, '-, ','')" />

As I understand it, the difficulty is that, given an aaa element, you
can't tell which of the many descendants has the latest date, so that
you can sort on that.

The easiest way to approach this, I think, is to make it a two-step
process. In the first step, create a copy of the source document, and
for each aaa element add a 'lastModified' attribute that gives the
latest date of their ancestor aaa elements, as follows:

<xsl:template match="aaa" mode="addLastModifiedAttribute">
  <aaa>
    <xsl:copy-of select="@*" />
    <xsl:attribute name="lastModified">
      <xsl:for-each select="descendant-or-self::aaa">
        <xsl:sort select="@vl" order="descending" />
        <xsl:if test="position() = 1">
          <xsl:value-of select="@vl" />
        </xsl:if>
      </xsl:for-each>
    </xsl:attribute>
    <xsl:apply-templates select="*" />
  </aaa>
</xsl:template>

You can store the result of this first step in a variable, through
something like:

  <xsl:variable name="aaaWithLastModified-rtf">
    <xsl:apply-templates select="aaa" mode="addLastModified" />
  </xsl:variable>

Then you can convert this variable into a node set using a node-set
extension function (I know it's not pure XSLT, but it's close
enough!), and process the aaa elements in the order of their
lastModified attribute:

  <xsl:apply-templates select="exsl:node-set($aaaWithLastModified-rtf)
                                 /aaa">
    <xsl:sort select="@lastModified" order="descending" />
  </xsl:apply-templates>

If you don't want to use an extension node-set function, then it's a
bit more complicated. I'd approach it with a recursive template that
accepts a bunch of aaa elements, locates the one with the latest date,
locates its ancestor amongst the aaa elements passed as the parameter,
applies templates to that ancestor, and then calls itself on a new set
of aaa elements that doesn't include that ancestor or any of its
descendants, as follows:

<xsl:template name="latestMessage">
  <xsl:param name="messages" select=".//aaa" />
  <xsl:for-each select="$messages">
    <xsl:sort select="@vl" />
    <xsl:if test="position() = 1">
      <xsl:variable name="latest" select="." />
      <xsl:variable name="ancestor"
        select="$latest/ancestor::aaa[count(.|$messages) =
                                      count($messages)]" />
      <xsl:apply-templates select="$ancestor" />
      <xsl:variable name="remainder"
        select="$messages[not(ancestor-or-self::aaa
                                [generate-id() =
                                 generate-id($ancestor)])]" />
      <xsl:if test="$remainder">
        <xsl:call-template name="latestMessage">
          <xsl:with-param name="messages"
                          select="$remainder" />
        </xsl:call-templates>
      </xsl:if>
    </xsl:if>
  </xsl:for-each>
</xsl:template>

This is likely to be very time-consuming. You could improve the speed
by using another method to find the latest message, rather than the
sort-and-select-the-first method.

Cheers,

Jeni

---
Jeni Tennison
http://www.jenitennison.com/


 XSL-List info and archive:  http://www.mulberrytech.com/xsl/xsl-list



Current Thread
Keywords