XSLT 2.0で便利になった機能(26) ソーティング

xsl:sortはXSLT1.0と比べてパラメータにcollationとstableが追加されていますが基本は変わっていません.しかし、selectで指定する式の自由度がXSLT2.0になって高くなっています.またXSLT1.0ではノードのソートしかできませんでしたが、XSLT2.0では任意のsequenceのソートができるようになっています.
 
■ XSLT1.0ではできなかったソートキーの動的選択
 
XSLT1.0では、xsl:sortのselect属性に書ける式には限界がありました.XSLT2.0では自由度が高くなったために、例えば次のようにDocBookの索引のソートを行うことができます.
 
<xsl:variable name="index_data">
    <xsl:for-each select="//indexterm">
        <xsl:sort select="if (primary/@sortas) then string(primary/@sortas) else string(primary)"/>
        <xsl:sort select="if (secondary/@sortas) then string(secondary/@sortas) else string(secondary)"/>
        <xsl:sort select="if (tertiary/@sortas) then string(tertiary/@sortas) else string(tertiary)"/>
        <xsl:copy-of select="."/>
    </xsl:for-each>
</xsl:variable>
 
DocBookでは、indextermの索引項目は順にprimary, secondary, tertiaryとあって、通常キーはそれらのテキスト値ですが、属性の@sortasがあるとそれをキーにしてソートしなければなりません.このように、ダイナミックにソートキーの選択を変えるのにif式は最適です.
 
このようなxsl:sortの羅列は、DocBookではOKですが、DITAではダメです.DITAの場合、DocBookより索引がさらに拡張されていて、次のように任意にネストした索引を作れてしまうからです.
 
<indexterm>cheese
    <indexterm>sheeps milk
        <indexterm>pecorino</indexterm>
    </indexterm>
    <indexterm>goats milk
        <indexterm>chevre</indexterm>
    </indexterm>
</indexterm>
 
DITAの方の索引は非常にやっかいで、DocBookのように一筋縄では行きません.
 
■ シーケンスのソート
 
XSLT2.0ではノードだけでなく一般的なシーケンスのソートもできるようになっています.ノードのソートでは、xsl:apply-templatesかxsl:for-eachの下にxsl:sortを記述しました.シーケンスのソートの場合は、xsl:perform-sortの下に書きます.
例えば、文書中の任意の要素に付くmodel属性を重複を省いて取り出してソートして結果を表示するのは次のようにできます.
 
<xsl:variable name="sortedModel" as="xs:string*">
    <xsl:perform-sort select="distinct-values(//*/@model)">
        <xsl:sort select="."/>
    </xsl:perform-sort>
</xsl:variable>
<xsl:template match="/">
    <xsl:for-each select="$sortedModel">
      <xsl:message>model="<xsl:value-of select="."/>"</xsl:message>
    </xsl:for-each>
</xsl:template>
 
シーケンスのソートは便利です.特にdistinct-values()と組み合わせると応用が効くのではないかと思います.