Создание алфавитного указателя с фиксированным количеством столбцов в XSLT 2.0

У меня есть XML, как это:

<xml version="1.0" encoding="UTF-8"?>
<countries>
    <country>
       <name>Latvia</name>   
    </country>
    <country>
       <name>USA</name>
    </country>
    <country>
       <name>Australia</name>
    </country>
    <country>
       <name>Indonesia</name>
    </country>
    <country>
       <name>UK</name>
    </country>
    <country>
       <name>India</name>
    </country>
    <country>
       <name>Argentina</name>
    </country>
    <country>
       <name>Chile</name>
    </country>
    <country>
       <name>Singapore</name>
    </country>
    <country>
       <name>New Zeland</name>
    </country>
    <country>
       <name>Kenya</name>
    </country>
    <country>
       <name>Zambia</name>
    </country>
    <country>
       <name>Tunisia</name>
    </country>
</countries>

Теперь я хочу создать алфавитный указатель стран в 3 столбца. Каждый столбец будет содержать число альфа как одну треть от присутствующего начального алфавита и его соответствующих стран.

В последнем столбце могут быть оставшиеся, если количество начальных алфавитов не делится на 3.

Например, здесь у нас есть названия стран, начинающиеся с L, C, U, A, I, S, N, K, Z и T.

После аранжировки: A C I K L N S T U Z

Теперь мой индекс будет иметь:

         Column1: A, C and I countries

         Column2: K, L and N countries

         Column3: S, T, U and Z countries

Следовательно, желаемый результат:

<countries>
  <column1> 
     <A>
        <country>
           <name>Argentina</name>
        </country>
        <country>
          <name>Australia</name>
        </country>
     </A>
     <C>
        <country>
           <name>Chile</name>
        </country>
     </C>
     <I>
        <country>
           <name>India</name>
        </country>
        <country>
          <name>Indonesia</name>
        </country>
     </I>   
  </column1>

  <column2> 
     <K>
        <country>
           <name>Kenya</name>
        </country>      
     </K>
     <L>
        <country>
           <name>Latvia</name>
        </country>

     </L>
     <N>
        <country>
           <name>New Zeland</name>
        </country>
     </N>   
  </column2>

  <column3> 
     <S>
        <country>
           <name>Singapore</name>
        </country>      
     </S>
     <T>
        <country>
           <name>Tunisia</name>
        </country>

     </T>
     <U>
        <country>
           <name>UK</name>
        </country>
        <country>
           <name>USA</name>
        </country>
     </U>   
     <Z>
        <country>
           <name>Zambia</name>
        </country>
     </Z>   
  </column3>
</countries>

Пожалуйста помоги. Я использую xslt 2.0.

2 ответа

Решение

Я придумал

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

<xsl:output indent="yes"/>

<xsl:param name="size" select="3"/>

<xsl:template match="countries">
    <xsl:copy>
      <xsl:variable name="groups">
        <xsl:for-each-group select="country" group-by="substring(name, 1, 1)">
            <xsl:sort select="current-grouping-key()"/>
            <xsl:element name="{current-grouping-key()}">
                <xsl:copy-of select="current-group()"/>
            </xsl:element>            
        </xsl:for-each-group>
      </xsl:variable>
      <xsl:for-each select="$groups/*[position() mod $size eq 1]">
        <xsl:if test="position() le $size">
          <xsl:variable name="pos" select="position()"/>
          <xsl:element name="column{position()}">
            <xsl:copy-of select="., following-sibling::*[if ($pos eq $size) then true() else (position() lt $size)]"/>
          </xsl:element>
        </xsl:if>
      </xsl:for-each>
    </xsl:copy>
</xsl:template>


</xsl:transform>

Я хотел бы рассмотреть возможность сделать это в два прохода, сначала сортировать и группировать по буквам, давая последовательность A - Z элементы, а затем разбить эту последовательность на столбцы.

<xsl:variable name="groups" as="element()*">
  <xsl:for-each-group select="/countries/country" group-by="substring(name, 1, 1)">
    <!-- sort groups alphabetically -->
    <xsl:sort select="current-grouping-key()" />
    <xsl:element name="{current-grouping-key()}">
      <xsl:perform-sort select="current-group()">
        <!-- sort names within each group -->
        <xsl:sort select="name" />
      </xsl:perform-sort>
    </xsl:element>
  </xsl:for-each-group>
</xsl:variable>

<xsl:variable name="numPerCol" select="count($groups) div 3" />
<column1>
  <xsl:sequence select="$groups[position() le $numPerCol]"/>
</column1>
<column2>
  <xsl:sequence select="$groups[position() gt $numPerCol and position() le (2*$numPerCol)]" />
</column2>
<column3>
  <xsl:sequence select="$groups[position() gt (2*$numPerCol)]" />
</column3>
Другие вопросы по тегам