1

I have a situation where I have to perform a sort according to the contents of a variable.

In XSLT 2.0 I do:

<xsl:sort select=" 
        if ($column = 'name') then name
        else if ($column = 'score') then count(//scores/score[@id=current()/@id])
        else if ($column = 'rating) then count(//ratings/rating[@id=current()/@id])
        else name"
        order={$sort}" />

But I need to do for version 1.0 and I can not find an alternative. How should I do?

3 Answers3

3

You can define multiple sort keys and arrange for some of them to be ineffective (by giving all nodes the same value for that sort key)

<xsl:sort select="name[$column = 'name']"/>
<xsl:sort select="count(/self::node()
          [$column = 'score']/scores/score[@id=current()/@id])"/>
<xsl:sort select="count(/self::node()
          [$column = 'rating']/ratings/rating[@id=current()/@id])"/>
<xsl:sort select="name"/> 
Michael Kay
  • 156,231
  • 11
  • 92
  • 164
  • Very cool. Would those "ineffective" sorts typically be O(1) or O(N)? – harpo Nov 01 '13 at 14:59
  • I would expect most implementations to only perform one sort, probably evaluating all the sort keys for every element, and comparing keys until it finds one that's different. So there will certainly be an extra cost for evaluating and comparing sort keys compared with the 2.0 solution, but it's probably not very high. Any decent processor will have a sort whose performance is O(N log N). – Michael Kay Nov 01 '13 at 15:20
1

This is definitely ugly, but may be one of the more compact ways to pull this off:

  <xsl:key name="score" match="scores/score" use="@id" />
  <xsl:key name="rating" match="ratings/rating" use="@id" />

  <!-- ... -->

  <xsl:variable name="useName" 
                select="$column != 'score' and $column != 'rating'" />
  <xsl:apply-templates select="something">
    <xsl:sort order="{$sort}" 
              data-type="{substring('numbertext', 1 + 6 * $useName, 6)}"
              select="concat(
                         substring(count(key('score', @id])), 1, 
                                   100 * ($column = 'score')),
                         substring(count(key('rating', @id])), 1, 
                                   100 * ($column = 'rating')),
                         substring(name, 1, 100 * $useName)
                      )"
              />
  </xsl:apply-templates>
JLRishe
  • 99,490
  • 19
  • 131
  • 169
0

When I have this situation in XSLT 1.0, I use an xsl:choose. Of course, this means you have to repeat the thing you're sorting (the apply-templates or for-each), which is sub-optimal.

If you really don't want to do that, you could also create a node-set that includes the sort key. That requires the node-set() extension function which is supported in most implementations (that I've used, anyway).

There is a construct for doing inline conditionals in XPath 1.0: see this answer from Tomalak. I haven't used it myself, even for binary conditions, and I'd worry about anyone who used it for a three-way condition. But it's your call.

Community
  • 1
  • 1
harpo
  • 41,820
  • 13
  • 96
  • 131