Search code examples
xslt

XSLT: find the node with an attribute value "lower than but closest to" my input value


I have an XML that contains a series of topic nodes that have a version attribute. Versioning is numeric. The topics are linked: we have topic nodes with the same ID, and different versions.

I want to select one of these topic nodes: the topic whose version is closest to, and equal or lower than, my input variable.

XML:

<topics>
  <topic id="1" version="6"/>
  <topic id="1" version="3"/>
  <topic id="1" version="1"/>
</topics>

If my input variable is 7, I want to select the topic with version="6". If my input variable is 6, I want to select the topic with version="6". If my input variable is 5, I want to select the topic with version="3".

Something like this would work:

<xsl:variable name="ver" select="7"/>
<xsl:choose>
    <xsl:when test="//topic[@version = $ver]">
        <xsl:copy-of select="//topic[@version = $ver]">
    </xsl:when>
    <xsl:when test="//topic[@version = $ver -1]">
        <xsl:copy-of select="//topic[@version = $ver -1]">
    </xsl:when>
    ...
</xsl:choose>

But I have 16 values for 'version' and there will be more in the future. Is there a way to replace this choose/when with something that does not have to enumerate every possible value?

(using Saxon, can use XSLT 2 and 3)


Solution

  • In XSLT 2 and later you can use <xsl:variable name="max" select="max(//topic/@version[. &lt; $ver])"/> and then select //topic[@version = $max].

    In XSLT 4 there is/will be a function highest https://qt4cg.org/specifications/xpath-functions-40/Overview.html#func-highest so that you could select e.g. highest(//topic[@version < $ver], (), function($t) { $t/@version }) in XPath or highest(//topic[@version &lt; $ver], (), function($t) { $t/@version }) in XSLT.