通过key和position()对XSLT进行分组和排序

问题描述:

我正在尝试显示按字母顺序排序的数据,以使以相同字母开头的项目位于单独的列中.在开始新列之前,这些列最多可容纳10个项目.我可以按字母顺序成功地对数据进行划分,并按每列的项目数进行划分,但是我正在努力将2:

I am trying to display data sorted alphabetically so that items that begin with the same letter are in separate columns. These columns can hold a maximum of 10 items before a new column is started. I can successfully divide the data up alphabetically AND divide it up by number of items per column but I am struggling to combine the 2:

按字母顺序划分:

<xsl:template match="/">
<xsl:key name="node-by-first-letter" match="node" use="substring(@email, 1, 1)" />


<div class="scroller-panel">
    <xsl:for-each select="msxml:node-set($members)/node[count(. | key('node-by-first-letter', substring(@email, 1, 1))[1]) = 1]">
        <xsl:sort select="@email" order="ascending"/>

            <xsl:apply-templates select="." mode="group" />
</xsl:for-each></div></xsl:template>
<xsl:template match="node" mode="group">
    <div class="column-312 scroller-item people-search-column fade-panel">
    <h2>
        <xsl:value-of select="Exslt.ExsltStrings:uppercase(substring(@email,1,1))"/>
    </h2>
    <ul class="stripe-list">
        <xsl:apply-templates select="key('node-by-first-letter', substring(@email, 1, 1))" mode="item">
            <xsl:sort select="@email" />
        </xsl:apply-templates>    
    </ul>
    </div>
</xsl:template>
<xsl:template match="node" mode="item">
            <li>
                <a href="4.0.1.person.profile.html">
                    <xsl:value-of select="@email"/>
                </a>
            </li>
</xsl:template>

除以每列的最大项数:

<xsl:for-each select="msxml:node-set($members)/members/member[position() mod 10 = 1]">
<ul>
<xsl:for-each select=". | following-sibling::*[not(position()    >=   10)]">
<li>
<xsl:value-of select="@email"/>
</li>
</xsl:for-each>
</ul>
</xsl:for-each>

首选输出如下:

http://rookery9.aviary.com.s3.amazonaws. com/9676500/9676792_3580_625x625.jpg

I. 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"
     exclude-result-prefixes="xs">
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:param name="pColLength" as="xs:integer" select="10"/>

 <xsl:template match="/*">
     <names>
       <xsl:for-each-group select="name"
                           group-by="substring(.,1,1)">
        <xsl:sort select="current-grouping-key()"/>
         <xsl:for-each-group select="current-group()"
          group-by="(position()-1) idiv $pColLength">
          <column>
            <xsl:copy-of select="current-group()"/>
          </column>
         </xsl:for-each-group>
       </xsl:for-each-group>
     </names>
 </xsl:template>
</xsl:stylesheet>

应用于此XML文档(因为问题中未提供!)

when applied on this XML document (as no such was provided in the question!!!):

<names>
        <name>T A</name>
        <name>T B</name>
        <name>T C</name>
        <name>T D</name>
        <name>T E</name>
        <name>T F</name>
        <name>T G</name>
        <name>T H</name>
        <name>T I</name>
        <name>T J</name>
        <name>T K</name>
        <name>T L</name>
        <name>A A</name>
        <name>A B</name>
        <name>A C</name>
        <name>A D</name>
        <name>A E</name>
        <name>A F</name>
        <name>A G</name>
        <name>A H</name>
        <name>A I</name>
        <name>A J</name>
        <name>A K</name>
        <name>A L</name>
        <name>X A</name>
        <name>X B</name>
        <name>X C</name>
        <name>X D</name>
        <name>X E</name>
        <name>X F</name>
        <name>X G</name>
        <name>X H</name>
        <name>X I</name>
        <name>X J</name>
        <name>X K</name>
        <name>X L</name>
        <name>R A</name>
        <name>R B</name>
        <name>R C</name>
        <name>R D</name>
        <name>R E</name>
        <name>R F</name>
        <name>R G</name>
        <name>R H</name>
        <name>R I</name>
        <name>R J</name>
        <name>R K</name>
        <name>R L</name>
</names>

产生所需的输出-名称以首字母开头排序,并放入每列10个项目的列中:

<names>
   <column>
      <name>A A</name>
      <name>A B</name>
      <name>A C</name>
      <name>A D</name>
      <name>A E</name>
      <name>A F</name>
      <name>A G</name>
      <name>A H</name>
      <name>A I</name>
      <name>A J</name>
   </column>
   <column>
      <name>A K</name>
      <name>A L</name>
   </column>
   <column>
      <name>R A</name>
      <name>R B</name>
      <name>R C</name>
      <name>R D</name>
      <name>R E</name>
      <name>R F</name>
      <name>R G</name>
      <name>R H</name>
      <name>R I</name>
      <name>R J</name>
   </column>
   <column>
      <name>R K</name>
      <name>R L</name>
   </column>
   <column>
      <name>T A</name>
      <name>T B</name>
      <name>T C</name>
      <name>T D</name>
      <name>T E</name>
      <name>T F</name>
      <name>T G</name>
      <name>T H</name>
      <name>T I</name>
      <name>T J</name>
   </column>
   <column>
      <name>T K</name>
      <name>T L</name>
   </column>
   <column>
      <name>X A</name>
      <name>X B</name>
      <name>X C</name>
      <name>X D</name>
      <name>X E</name>
      <name>X F</name>
      <name>X G</name>
      <name>X H</name>
      <name>X I</name>
      <name>X J</name>
   </column>
   <column>
      <name>X K</name>
      <name>X L</name>
   </column>
</names>

说明:

  1. 嵌套的 xsl:for-each-group -首先按起始字符分组,然后对每个这样确定并排序的组进行分组-按其项应在其中的列号进行.

  1. Nested xsl:for-each-group -- first grouped by the starting character, then for each such determined and sorted group -- by the number of the column in which its items should be.

使用标准XSLT 2.0函数 current-grouping-key() current-group() .

II.XSLT 1.0解决方案:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:param name="pColLength" select="10"/>

 <xsl:key name="kStarting" match="name"
  use="substring(.,1,1)"/>

 <xsl:template match="/*">
  <names>
          <xsl:for-each select=
           "name
              [generate-id()
              =
               generate-id(key('kStarting', substring(.,1,1))[1])
              ]
           ">
            <xsl:sort select="substring(.,1,1)"/>

            <xsl:variable name="vgroupNames" select=
               "key('kStarting', substring(.,1,1))"/>

            <xsl:apply-templates select="$vgroupNames[1]">
              <xsl:with-param name="pGroup" select="$vgroupNames"/>
              <xsl:with-param name="pGroupLength" select=
               "count($vgroupNames)"/>
            </xsl:apply-templates>
          </xsl:for-each>
  </names>
 </xsl:template>

 <xsl:template match="name">
   <xsl:param name="pGroup"/>
   <xsl:param name="pGroupLength"/>
   <xsl:param name="pInd" select="1"/>

   <xsl:if test="not($pInd > $pGroupLength)">
      <column>
       <xsl:copy-of select=
       "$pGroup
           [position() >= $pInd
          and
            not(position() > $pInd + $pColLength -1)
            ]"/>
      </column>

      <xsl:apply-templates select=
        "$pGroup[position() = $pInd + $pColLength]">
       <xsl:with-param name="pGroup" select="$pGroup"/>
        <xsl:with-param name="pGroupLength" select="$pGroupLength"/>
        <xsl:with-param name="pInd" select="$pInd + $pColLength"/>
       </xsl:apply-templates>
   </xsl:if>
 </xsl:template>
</xsl:stylesheet>

应用于相同的XML文档(如上所述)时,会产生相同的期望输出-名称以首字母开头并放入每行10个项目的列中:

when applied on the same XML document (as above), the same desired output is produced -- names sorted by starting first letter and put into columns of 10 items each:

<names>
   <column>
      <name>A A</name>
      <name>A B</name>
      <name>A C</name>
      <name>A D</name>
      <name>A E</name>
      <name>A F</name>
      <name>A G</name>
      <name>A H</name>
      <name>A I</name>
      <name>A J</name>
   </column>
   <column>
      <name>A K</name>
      <name>A L</name>
   </column>
   <column>
      <name>R A</name>
      <name>R B</name>
      <name>R C</name>
      <name>R D</name>
      <name>R E</name>
      <name>R F</name>
      <name>R G</name>
      <name>R H</name>
      <name>R I</name>
      <name>R J</name>
   </column>
   <column>
      <name>R K</name>
      <name>R L</name>
   </column>
   <column>
      <name>T A</name>
      <name>T B</name>
      <name>T C</name>
      <name>T D</name>
      <name>T E</name>
      <name>T F</name>
      <name>T G</name>
      <name>T H</name>
      <name>T I</name>
      <name>T J</name>
   </column>
   <column>
      <name>T K</name>
      <name>T L</name>
   </column>
   <column>
      <name>X A</name>
      <name>X B</name>
      <name>X C</name>
      <name>X D</name>
      <name>X E</name>
      <name>X F</name>
      <name>X G</name>
      <name>X H</name>
      <name>X I</name>
      <name>X J</name>
   </column>
   <column>
      <name>X K</name>
      <name>X L</name>
   </column>
</names>

说明:

  1. 使用孟买分组方法 ,再加上排序,我们获得(按排序顺序)每组name元素,这些元素由以相同字符开头的所有名称组成.

  1. Using the Muenchian grouping method, plus sorting, we obtain (in sorted order) each group of name elements consisting of all names starting with the same character.

通过将模板应用于第一个name元素来处理上述每个name元素.整个组,其长度和该组中name元素的索引(默认= 1)作为参数传递.

Every group of name elements as obtained above is processed by applying templates to its first name element. The whole group, its length and the index of the name element in the group (default = 1) are passed as parameters.

保证与name元素匹配的模板仅应用于列中的起始元素.它会创建一个新的column元素,并在其中复制此列的所有name元素(从索引$pInd开始,到索引$pInd+$pColLength -1结束.不要求这些元素应该是同级元素(它们不是's'). t).如果每个name不仅需要复制,还需要其他处理,则可以在此处通过将<xsl:copy-of>指令替换为:

The template matching a name element is guaranteed to be applied only on a starting element within a column. It creates a new column element and copies in it all name elements for this column (starting from index $pInd and ending at index $pInd+$pColLength -1. There is no requirement that these elements should be siblings (and they aren't). If not just copying but also additional processing is required for each name, this can be done here by replacing the <xsl:copy-of> instruction with:

-

<xsl:apply-templates mode="process" select=
           "$pGroup
               [position() >= $pInd
              and
                not(position() > $pInd + $pColLength -1)
                ]"/>