Группировка и сортировка XSLT по ключу и позиции () - PullRequest
1 голос
/ 04 сентября 2011

Я пытаюсь отобразить данные, отсортированные в алфавитном порядке, чтобы элементы, начинающиеся с одной и той же буквы, находились в отдельных столбцах. Эти столбцы могут содержать не более 10 элементов перед началом нового столбца. Я могу успешно разделить данные по алфавиту И разделить их на количество элементов в столбце, но я изо всех сил пытаюсь объединить 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

1 Ответ

4 голосов
/ 05 сентября 2011

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-документу (как таковое не было предоставлено в вопросе !!!):

<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 - сначала сгруппированный по начальному символу, затем для каждой такой определенной и отсортированной группы - по номеру столбца, в которомдолжны быть следующие элементы:

  2. Использование стандартных функций 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 элементов в каждом :

<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 элементов, состоящих из всех имен, начинающихся с одного и того же символа.

  2. Каждая группа элементов name, полученная выше, обрабатывается путем применения шаблонов к ее первому элементу name .Вся группа, ее длина и индекс элемента name в группе (по умолчанию = 1) передаются в качестве параметров.

  3. Шаблон, соответствующий nameэлемент гарантированно будет применяться только к начальному элементу в столбце .Он создает новый элемент column и копирует в него все элементы name для этого столбца (начиная с индекса $pInd и заканчивая индексом $pInd+$pColLength -1. Нет необходимости, чтобы эти элементы были родными (и они не являются ')t). Если для каждого name требуется не только копирование, но и дополнительная обработка, это можно сделать здесь, заменив инструкцию <xsl:copy-of> на:

-

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