开发者

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜