-1

リストから 4 種類のデータを選択し、テーブルにグループ化する必要があります。XML 入力の例は次のようになります。

<DIV>
  <ul>
    <li>fr0.1.1 : en1.1.1</li>
    <li>fr0.2.1 : en1.2.1</li>
    <li>fr0.4.1 : en1.3.1</li>
    <li>fr0.6.1 : en1.4.1</li>
    <li>fr0.5.1 : en1.5.1</li>
    <li>fr.0.7.1 : en1.5.1</li>
    <li>        : en1.6.1</li>
  </ul
</DIV>

同じ接頭辞 (例: 'fr') を持つ各 3 桁の数字の 2 桁目は、前後の 3 桁の数字の 2 桁目と相対的です。たとえば、fr0.1.1、fr0.2.1、fr0.3.1 は連続です。 fr0.1.1、fr0.3.1、fr0.2.1 または fr0.1.1、fr0.3.1 は不連続です。

各 <li> 要素の左側にある「fr」で始まる数字の間でのみ、これらの関係のギャップと不一致を見つけるコードが必要です。このようなシーケンスに欠落している番号がある場合、コードはそれを取得し、「Absent(fr)」という名前の最初の列にリストする必要があります。左側のすべての 3 桁の数字のリストを下に移動するときに 2 桁目が昇順で配置されていない場合、コードは、2 桁目が昇順の順序で矛盾している数字を識別する必要があります。たとえば、fr0 です。上記の入力例では、(0.)4(.1)<(0.)6(.1)>(0.)5(.1) の場合は .6.1 です。そのような数が見つかった場合は、「Discrepant」と呼ばれる 2 番目の列にリストする必要があります。3 番目の列には、矛盾する数値が一致する数値が右側に表示されます。

「No-match(en)」と呼ばれる 4 番目の列には、右側に「fr」で始まる一致がない場合にのみ、「en」で始まる番号をリストする必要があります。この例では、そのような数値は 1.6.1 です。

入力リストに複数回出現する場合のみ、「繰り返し」と表示された 5 番目の列に「en」で始まる番号をリストする必要があります。そして、コードは、繰り返し番号の「fr」で始まる一致を取得し、最後の列「一致」にリストする必要があります。

HTML 出力の表は次のようになります (色や太字などのスタイルは重要ではありません!)。

テーブル

xml 出力は次のようになります。

<table>
 <tr>   
   <th>Absent(en)</th>
   <th>Discrepant</th>
   <th>Match</th>
   <th>No-match(en)</th>
   <th>Repetition</th>
  <th>Matches</th>
 </tr>
 <tr>
   <td>0.3.1</td>
   <td>0.6.1</td>
   <td>1.4.1</td>
   <td>1.6.1</td>
   <td>1.5.1</td>
   <td>0.5.1, 0.7.1</td>
 </tr>
</table>
4

1 に答える 1

0

私はあなたのすべての質問に答えません。StackOverflow には少し多すぎます。私がすることは、あなたの質問の核となる部分に答えることです (最初の列)。これは、完全な答えを得るために外挿するのに十分なはずです. 私の答えから推測するのに問題がある場合は、問題の未解決の部分を小さなタスクに分割し、もちろん、自分でうまく試した後、それぞれに個別の SO 質問を投稿してください。

この入力文書は...(形成エラーを修正し、最後のliのケースを処理しないために、あなたのものとは少し異なります)

<DIV>
  <ul>
    <li>fr0.1.1 : en1.1.1</li>
    <li>fr0.2.1 : en1.2.1</li>
    <li>fr0.4.1 : en1.3.1</li>
    <li>fr0.6.1 : en1.4.1</li>
    <li>fr0.5.1 : en1.5.1</li>
    <li>fr0.7.1 : en1.5.1</li>
  </ul>
</DIV>

... この XSLT 2.0 スタイルシートに適用すると ...

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:so="http://stackoverflow.com/questions/17776650"
  exclude-result-prefixes="xsl xs fn so">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />

<xsl:function name="so:middle-number" as="xs:integer">
 <xsl:param name="dotted-text" as="xs:string" />
 <xsl:sequence select="fn:replace( $dotted-text, 'fr0\.(\d+)\.1.*', '$1') cast as xs:integer" />
</xsl:function>

<xsl:function name="so:delta" as="xs:integer">
 <!-- Difference between this node and the previous.
      Count the first node as having difference = 1 . -->
 <xsl:param name="li" as="element()" />
 <xsl:sequence select="
   if ($li/preceding-sibling::li) then
       so:middle-number($li/text()) - so:middle-number($li/preceding-sibling::li[1]/text())
     else
       1" />
</xsl:function>

<xsl:function name="so:in-between" as="xs:integer*">
 <xsl:param name="lower-bound" as="xs:integer" />
 <xsl:param name="upper-bound" as="xs:integer" />
 <xsl:variable name="diff" select="$upper-bound - $lower-bound" />
 <xsl:choose>
  <xsl:when test="$diff eq 0"> 
    <xsl:sequence select="$lower-bound" />
  </xsl:when>
  <xsl:when test="$diff eq 1"> 
    <xsl:sequence select="$lower-bound, $lower-bound+1" />
  </xsl:when>
  <xsl:when test="($diff ge 2)"> 
    <xsl:variable name="half-way" select="fn:round( $lower-bound + ($diff div 2))" />
    <xsl:sequence select="so:in-between($lower-bound, $half-way) , so:in-between( $half-way+1, $upper-bound)" />
  </xsl:when>
  <xsl:otherwise />   
 </xsl:choose>
</xsl:function>

<xsl:template match="/*/ul">
 <xsl:variable name="groups">
  <xsl:for-each-group select="li" group-adjacent="so:delta(.)">
   <so:group>
    <so:start><xsl:value-of select="so:middle-number( current-group()[1     ]/text())" /></so:start>
    <so:end>  <xsl:value-of select="so:middle-number( current-group()[last()]/text())" /> </so:end>
   </so:group>
  </xsl:for-each-group>
 </xsl:variable>
 <xsl:apply-templates select="$groups" /> 
</xsl:template>

<xsl:template match="so:group[1]" />

<xsl:template match="so:group">
  <xsl:variable name="this" select="so:start/text() cast as xs:integer" as="xs:integer" />
  <xsl:variable name="prev" select="preceding-sibling::so:group[1]/so:end/text() cast as xs:integer" as="xs:integer" />
  <xsl:for-each select="for $x in so:in-between( $prev + 1, $this - 1) return $x">
   <td><xsl:value-of select="concat('0.',.,'.1')" /></td>
  </xsl:for-each> 
</xsl:template>

</xsl:stylesheet>

...結果が得られます...

<td>0.3.1</td>
<td>0.6.1</td>

.. これは、2 つの存在しない li 値です。

お持ち帰りポイント

xsl:for-each-group/@group-adjacent 命令を使用して、1 つの li ノードの値とその前のノードの間のデルタでグループ化できます。これにより、連続したノードのグループが得られます。したがって、あるグループの終わりと次のグループの始まりの間の差をスパンするだけで、すべての欠落した値が得られます。これにより、最初の列が得られます。

他の列にも同様の手法を使用します。


アップデート

so:in between() 関数が必要ないことに気付きました。単純に演算子を使用できますto。申し訳ありません。演算子に変更するとto、スタイルシートが大幅に簡素化されます。


更新 2

これがtoバージョンです...私はそれが十分に小さいと思います!

<xsl:stylesheet version="2.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:fn="http://www.w3.org/2005/xpath-functions"
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:so="http://stackoverflow.com/questions/17776650"
  exclude-result-prefixes="xsl xs fn so">
<xsl:output indent="yes" omit-xml-declaration="yes" />
<xsl:strip-space elements="*" />

<xsl:function name="so:middle-number" as="xs:integer">
 <xsl:param name="dotted-text" as="xs:string" />
 <xsl:sequence select="fn:replace( $dotted-text, 'fr0\.(\d+)\.1.*', '$1') cast as xs:integer" />
</xsl:function>

<xsl:function name="so:delta" as="xs:integer">
 <!-- Difference between this node and the previous.
      Count the first node as having difference = 1 . -->
 <xsl:param name="li" as="element()" />
 <xsl:sequence select="
   if ($li/preceding-sibling::li) then
       so:middle-number($li/text()) - so:middle-number($li/preceding-sibling::li[1]/text())
     else
       1" />
</xsl:function>

<xsl:template match="/*/ul">
 <xsl:variable name="groups">
  <xsl:for-each-group select="li" group-adjacent="so:delta(.)">
   <so:group>
    <so:start><xsl:value-of select="so:middle-number( current-group()[1     ]/text())" /></so:start>
    <so:end>  <xsl:value-of select="so:middle-number( current-group()[last()]/text())" /> </so:end>
   </so:group>
  </xsl:for-each-group>
 </xsl:variable>
 <xsl:apply-templates select="$groups" /> 
</xsl:template>

<xsl:template match="so:group[1]" />

<xsl:template match="so:group">
  <xsl:variable name="this" select="so:start/text() cast as xs:integer" as="xs:integer" />
  <xsl:variable name="prev" select="preceding-sibling::so:group[1]/so:end/text() cast as xs:integer" as="xs:integer" />
  <xsl:for-each select="for $x in $prev + 1 to $this - 1 return $x">
   <td><xsl:value-of select="concat('0.',.,'.1')" /></td>
  </xsl:for-each> 
</xsl:template>

</xsl:stylesheet>

XSLTが難しすぎることについて

XSLT が多すぎて理解できないというあなたのコメントに関連して、XSLT は他の言語と同じであることを覚えておいてください。StackOverflow に 1 つまたは 2 つの質問を投稿するだけでは、言語を理解することは期待できません。長期的には、あなたの利益は投資によって最もよく満たされると思います. XSLT 2 に関する優れた本を手に取って読んでください。適切な本を選べば、驚くほど簡単に学べる言語です。それが私が行ったことであり、私の投資に対する利益は十分であることがわかりました.

于 2013-07-22T13:34:02.437 に答える