xslt: Finding a value nearest to another value in a list
Greetings, given a list of numbers, is it possible to find a value closest to a given value with xslt? For example, if I'm looking for a value closest to 5 in a list [1,7] then 7 would be it. L开发者_开发技巧ikewise for [4,9], it would be 4. The list can be any length.
Thanks.
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="pValue" select="5"/>
<xsl:template match="/">
<xsl:for-each select="list/num">
<xsl:sort select="(. - $pValue) * not(0 > . - $pValue )
- (. - $pValue) * (0 > . - $pValue)"/>
<xsl:if test="position() = 1">
<xsl:value-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
With this input:
<list>
<num>4</num>
<num>9</num>
</list>
Output:
4
And this input:
<list>
<num>1</num>
<num>7</num>
</list>
Output:
7
EDIT: XSLT 2.0 solution:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="pValue" select="5"/>
<xsl:template match="/">
<xsl:variable name="vSequence"
select="/list/num/abs(. - $pValue)"/>
<xsl:variable name="vMinimum"
select="min($vSequence)"/>
<xsl:variable name="vPosition"
select="index-of($vSequence,$vMinimum)[1]"/>
<xsl:value-of select="/list/num[$vPosition]"/>
</xsl:template>
</xsl:stylesheet>
It shows that it could be one line XPath 2.0 expression:
/list/num[
index-of(
/list/num/abs(. - $pValue),
min(/list/num/abs(. - $pValue))
)[1]
]
I. XSLT 1.0 solution:
Because @Alejandro was quicker than I, I now had to devise another solution :)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" xmlns:my="my:my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:num>5.6</my:num>
<xsl:param name="pValue" select="document('')/*/my:num"/>
<xsl:variable name="vList" select="/*/*"/>
<xsl:template match="/">
<xsl:variable name="vrtfSorted">
<xsl:for-each select="$vList | $pValue">
<xsl:sort select="." data-type="number"/>
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="vSorted" select="ext:node-set($vrtfSorted)/*"/>
<xsl:variable name="vVal1" select=
"$vSorted[.=$pValue]/preceding-sibling::*[1]"/>
<xsl:variable name="vVal2" select=
"$vSorted[.=$pValue]/following-sibling::*[1]"/>
<xsl:value-of select=
"($pValue - $vVal1 > $vVal2 - $pValue) * $vVal2
+
(not($pValue - $vVal1 > $vVal2 - $pValue)) * $vVal1
"/>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document (containing the list of values):
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
6
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:param name="pNumber" as="xs:double" select="5.3"/>
<xsl:param name="pList" as="xs:double+"
select="3,2,5,7,8,6,4,9,1"/>
<xsl:template match="/">
<xsl:variable name="vSorted" as="xs:double+">
<xsl:perform-sort select="$pList">
<xsl:sort select="abs(. - $pNumber)"/>
</xsl:perform-sort>
</xsl:variable>
<xsl:sequence select="$vSorted[1]"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on any XML document (not used), the wanted, correct result is produced:
5
精彩评论