2

最近、私を助けてくれたこのコミュニティに感謝したいと思います。あなたは私の救世主です:)

数値 (x) が前の数値と異なる場合を確認しようとしてい
ます。リストが並べ替えられました。まず、次のようにします。

<xsl:apply-templates select="house">
    <xsl:sort select="./point/x"/>
</xsl:apply-templates>

重要な部分は次のとおりです。

<xsl:template match="house">

    <xsl:if test="./point/x != preceding-sibling::house[1]/point/x">
        <br/> 
        <xsl:value-of select="preceding-sibling::house[1]/point/x"/>
             value changed to <xsl:value-of select="./point/x"/>        
    </xsl:if>

    <!-- Just printing -->
    <br/>
    <td><xsl:value-of select="./point/x"/></td>
    <td><xsl:value-of select="./point/y"/></td>

</xsl:template>

印刷物には xと yの 2 つの値があることに注意してください。しかし、私は x だけを扱ってい ます
。出力を次のようにしたいと考えてい ます 。





どういうわけか私の結果は間違った前の番号を取得します

00
1 値を 0 に変更
01
0 値を 1 に変更
10
0 値を 1 に変更
11

進捗状況: 並べ替えを削除すると、出力が正しいことがわかりました

00
0 値を 1 に変更
10
1 値を 0
01に
変更 0 値を 1
11に変更

だから私の質問は...どうすればこの手順を実行できますか?

4

3 に答える 3

3

これは、はるかに効率的な (線形 vs O(N^2*log(N))) XSLT 1.0 ソリューションです。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

 <xsl:key name="kHouseById" match="house" use="generate-id()"/>

 <xsl:variable name="vIdsOfSorted">
  <xsl:for-each select="/*/house">
    <xsl:sort select="point/x" data-type="number"/>
    <xsl:copy-of select="concat(generate-id(), ' ')"/>
  </xsl:for-each>
 </xsl:variable>


 <xsl:template match="/*">
  <xsl:call-template name="traverseIds">
   <xsl:with-param name="pIds" select="concat($vIdsOfSorted, ' ')"/>
   <xsl:with-param name="pLength" select="count(house)"/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="traverseIds">
   <xsl:param name="pIds"/>
   <xsl:param name="pLength"/>
   <xsl:param name="pPos" select="1"/>
   <xsl:param name="pPrevId" select="''"/>

   <xsl:if test="not($pPos > $pLength)">
     <xsl:variable name="vId" select="substring-before($pIds, ' ')"/>
     <xsl:variable name="vHouse" select="key('kHouseById', $vId)"/>

     <xsl:value-of select=
      "concat('&#xA;(', $vHouse/point/x, ',', $vHouse/point/y, ')')"/>
       <xsl:if test="not($pPos >= $pLength)">
        <xsl:variable name="vNextId" select=
         "substring-before(substring-after($pIds, ' '), ' ')"/>

        <xsl:variable name="vNextHouse" select="key('kHouseById', $vNextId)"/>

        <xsl:if test="not($vHouse/point/x = $vNextHouse/point/x)">
         <xsl:value-of select=
           "concat('&#xA;value changed to ', $vNextHouse/point/x)"/>
        </xsl:if>

        <xsl:call-template name="traverseIds">
          <xsl:with-param name="pIds" select="substring-after($pIds, ' ')"/>
          <xsl:with-param name="pLength" select="$pLength"/>
                <xsl:with-param name="pPos" select="$pPos+1"/>
                <xsl:with-param name="pPrevId" select="$vId"/>
        </xsl:call-template>
       </xsl:if>
     </xsl:if>
 </xsl:template>
</xsl:stylesheet>

この変換が次の XML ドキュメントに適用される場合:

<t>
  <house>
    <point>
      <x>1</x><y>0</y>
    </point>
  </house>
  <house>
    <point>
      <x>0</x><y>1</y>
    </point>
  </house>
  <house>
    <point>
      <x>0</x><y>0</y>
    </point>
  </house>
  <house>
    <point>
      <x>1</x><y>1</y>
    </point>
  </house>
</t>

必要な正しい結果が生成されます。

(0,1)
(0,0)
value changed to 1
(1,0)
(1,1)

xxx:node-set()再帰が気に入らない場合は、拡張機能を使用する非再帰的な XSLT 1.0 ソリューションを次に示します。

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common">
 <xsl:output method="text"/>
 <xsl:variable name="vrtfSorted">
        <xsl:for-each select="/*/house">
            <xsl:sort select="point/x" data-type="number"/>
            <xsl:copy-of select="."/>
        </xsl:for-each>
 </xsl:variable>

 <xsl:variable name="vSorted" select="ext:node-set($vrtfSorted)/*"/>

    <xsl:template match="/*">
        <xsl:for-each select="$vSorted">
            <xsl:variable name="vPos" select="position()"/>
            <xsl:if test=
              "$vPos > 1 
             and 
               not(point/x = $vSorted[position()=$vPos -1]/point/x)">
                <xsl:text>&#xA;</xsl:text>
                <xsl:value-of select="concat('value changed to ', point/x)"/>
            </xsl:if>
            <xsl:text>&#xA;</xsl:text>
            <xsl:value-of select="concat('(',point/x,',',point/y, ')')"/>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

まったく同じ、正しい結果が生成されます


Ⅱ.XSLT 2.0 ソリューション

<xsl:stylesheet version="2.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:variable name="vSorted">
   <xsl:perform-sort select="house">
     <xsl:sort select="xs:integer(point/x)"/>
   </xsl:perform-sort>
  </xsl:variable>

  <xsl:sequence select=
   "(for $n   in 2 to count($vSorted/*),
        $n-1 in $n -1,
        $vNewX in $vSorted/*[$n]/point/x,
        $vOldX in $vSorted/*[$n-1]/point/x,
        $vNewY in $vSorted/*[$n]/point/y,
        $vOldY in $vSorted/*[$n-1]/point/y,
        $remark in
              if($vNewX ne $vOldX)
                then concat('&#xA;value changed to ', $vNewX, '&#xA;')
                else '&#xA;'
     return
       concat('(',$vOldX,',',$vOldY, ')', $remark)
     ),
     concat('(',$vSorted/*[last()]/point/x,',',
                $vSorted/*[last()]/point/y, ')'
            )
   "/>
 </xsl:template>
</xsl:stylesheet>

同じ XML ドキュメント (上記) に適用すると、同じ正しい結果が生成されます。

 (0,1)
 (0,0)
value changed to 1
 (1,0)
 (1,1)
于 2012-04-07T02:43:43.993 に答える
3

あまりいいことではありませんが、「特定の順序に従った x の前の値」を知る必要がある場合は、ノードを再度順序付けして、正しいノードをフェッチする必要があります。

<xsl:template match="house">
    <xsl:variable name="current-position" select="position()" />
    <xsl:variable name="preceding-x-ordered">
      <xsl:for-each select="../house">
        <xsl:sort select="point/x" />
        <xsl:if test="position() = $current-position - 1">
          <xsl:value-of select="point/x" />
        </xsl:if>
      </xsl:for-each>
    </xsl:variable>

    <xsl:if test="position() &gt; 1 and point/x != $preceding-x-ordered">
        <br/> 
        <xsl:value-of select="$preceding-x-ordered"/>
        <xsl:text> value changed to </xsl:text>
        <xsl:value-of select="point/x"/>        
    </xsl:if>

    <!-- Just printing -->
    <br/>
    <td><xsl:value-of select="point/x"/></td>
    <td><xsl:value-of select="point/y"/></td>
</xsl:template>

XSLT は副作用がないはずです。つまり、プロセッサは、全体的な結果を変更することなく、任意の順序でテンプレートを処理できます。ループの最後の繰り返しで何をしたかを「認識」していないのは、その要件の結果です。したがって、preceding-siblingどのノードが以前に処理されたかを軸が知る方法はありません。

于 2012-04-06T15:38:29.950 に答える
2

別のアプローチを次に示します。グループの観点から考えたいと思うかもしれません。質問 xslt 2.0 にタグを付けたので、これにはいくつかの便利なグループ化関数が付属しています (xslt 1.0 では、Meunchian Grouping テクニックを利用する必要があります)。

事実上、point/x値で家をグループ化しているので、次のようにそれぞれのグループを取得します。

<xsl:for-each-group select="house" group-by="point/x">
   <xsl:sort select="point/x"/>

次に、次のようにグループ内の家を反復処理できます。

<xsl:for-each select="current-group()">
    <xsl:sort select="point/y"/>

ここに完全な XSLT があります

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
   <xsl:output method="text"/>

   <xsl:template match="houses">
      <xsl:text>xy&#13;</xsl:text>
      <xsl:for-each-group select="house" group-by="point/x">
         <xsl:sort select="point/x"/>
         <xsl:if test="position() &gt; 1">
            <xsl:value-of select="concat(' value changed to ', point/x, '&#13;')" />
         </xsl:if>
         <xsl:for-each select="current-group()">
            <xsl:sort select="point/y"/>
            <xsl:value-of select="concat(point/x, point/y, '&#13;')" />
         </xsl:for-each>
         <xsl:if test="position() &lt; last()">
            <xsl:value-of select="point/x" />
         </xsl:if>
      </xsl:for-each-group>
   </xsl:template>
</xsl:stylesheet>

次の XML に適用すると....

<houses>
    <house><point><x>1</x><y>0</y></point></house>
    <house><point><x>2</x><y>1</y></point></house>
    <house><point><x>0</x><y>1</y></point></house>
    <house><point><x>1</x><y>1</y></point></house>
    <house><point><x>0</x><y>0</y></point></house>
    <house><point><x>2</x><y>0</y></point></house>
</houses>

以下が出力されます。

xy
00
01
0 value changed to 1
10
11
1 value changed to 2
20
21
于 2012-04-06T22:42:45.467 に答える