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: sorting a subset of elements


Hi,

> I have created an xml document with over 300 items sorted by 
> price.  For the
> internet I want to display all items, however for print I only want to
> display the 90 most expensive and i want them sorted 
> alpabetically.  I am
> not sure how to accomplish this.

You can't sort, filter and sort again with XSLT 1.0; using XSLT 1.1 or 2.0 you could sort the items into a variable and sort it again. So, either do two transformations, where you sort by price and filter in the first one, and sort by name in the second one; or write a recursive template that will select the first 50 most expensive items, but that will probably be quite inefficient, e.g. (should work)

<?xml version='1.0' encoding='UTF-8' ?>
<xsl:stylesheet version='1.0'
                xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>

<xsl:template match="itemlist">
  <xsl:copy>
    <xsl:call-template name="max-n">
      <xsl:with-param name="ns" select="item" />
    </xsl:call-template>
  </xsl:copy>
</xsl:template>

<xsl:template match="item">
  <xsl:copy-of select="." />
</xsl:template>

<xsl:template name="max-n">
  <xsl:param name="ns" select="/.." />
  <xsl:param name="selected" select="/.." />
  <xsl:param name="n" select="50"/>

  <xsl:choose>
    <xsl:when test="not(count($ns) = 0) and count($selected) &lt; $n">
      <xsl:variable name="max">
        <xsl:call-template name="max">
          <xsl:with-param name="ns" select="$ns" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="tbs" select="$ns[@price = $max]" />
      <xsl:call-template name="max-n">
        <xsl:with-param name="ns" select="$ns[not(@price = $max)]" />
        <xsl:with-param name="selected" select="$selected | $ns[@price = $max][position() &lt; ($n + 1 - count($selected))]" />
      </xsl:call-template>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="$selected">
        <xsl:sort select="@description" />
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template name="max">
  <xsl:param name="ns" select="/.." />

  <xsl:choose>
    <xsl:when test="count($ns) > 2">
      <xsl:variable name="c" select="ceiling(count($ns) div 2)" />
      <xsl:variable name="head">
        <xsl:call-template name="max">
          <xsl:with-param name="ns" select="$ns[position() &lt;= $c]" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="tail">
        <xsl:call-template name="max">
          <xsl:with-param name="ns" select="$ns[position() > $c]" />
        </xsl:call-template>
      </xsl:variable>
      <xsl:choose>
        <xsl:when test="$head > $tail">
          <xsl:value-of select="$head" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$tail" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:when test="count($ns) > 1">
      <xsl:choose>
        <xsl:when test="$ns[1]/@price > $ns[2]/@price">
          <xsl:value-of select="$ns[1]/@price" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="$ns[2]/@price" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$ns/@price" />
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

</xsl:stylesheet>

But you do not want that with 300 items, especially if just about every item has a unique price; so go with the two (chained) transformations.

Cheers,

Jarno

 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]