Reputation: 2564
I want to sort these elements in ascending order but with the nulls last rather than first which seems to be what xslt does by default. I've managed to do this but wonder if there is a better way. Here's my example.
<b>
<c>2</c>
<c>1</c>
<c>3</c>
<c></c>
<c>15</c>
<c>11</c>
<c></c>
<c>43</c>
<c>4</c>
</b>
<xsl:template match="/">
<xsl:for-each select="b/c">
<xsl:sort select="node() = false()"/>
<xsl:sort select="." data-type="number"/>
Row<xsl:value-of select="position()"/>:<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
Which gives the required output of:
Row1:1
Row2:2
Row3:3
Row4:4
Row5:11
Row6:15
Row7:43
Row8:
Row9:
I'm currently using <xsl:sort select="node() = false()"/>
to test for it being null then using a sort to order the null elements last (null will be 1 and not null will be 0 so it orders them correctly).
Can anyone suggest anything better than this?
Upvotes: 4
Views: 3706
Reputation: 338336
<xsl:template match="/">
<xsl:for-each select="b/c">
<xsl:sort select="concat(
substring('1', 1, boolean(text())),
substring('0', 1, not(boolean(text())))
)" />
<xsl:sort select="." data-type="number"/>
<xsl:text>Row</xsl:text>
<xsl:value-of select="position()"/>
<xsl:text>:</xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
This:
concat(
substring('1', 1, boolean(text()) ),
substring('0', 1, not(boolean(text())))
)
Produces either '0' or '1', depending on whether there is a child text node, or not. It is the concatenation of two mutually exclusive strings - the poor man's if/then/else in XPath 1.0.
The boolean(text())
produces true
or false
, which is then converted to a number for substring()
. Boolean values are converted to 1 or 0, respectively.
The more complete version of the above goes like this:
concat(
substring(
$if_str,
1,
boolean($condition) * string-length($if_str)
),
substring(
$else_str,
1,
not(boolean($condition)) * string-length($else_str)
)
)
Upvotes: 5