Creating alphabetical index with fixed number of columns in XSLT 2.0

本秂侑毒 提交于 2019-12-12 04:07:01

问题


I have an XML like this:

<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>

Now I want to create alphabetical index of countries in 3 columns. Each column will contain the number of alpha as one third of the starting alphabets present and its corresponding countries.

Last column can have the rest of them if number of starting alphabets present happens to be not divisible by 3.

For example, here we have country names starting with L, C, U, A, I, S, N, K, Z and T.

After arranging: A C I K L N S T U Z

Now, my index will have:

         Column1: A, C and I countries

         Column2: K, L and N countries

         Column3: S, T, U and Z countries

Hence the desired output is:

<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>

Please help. I am using xslt 2.0.


回答1:


I came up with

<?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>



回答2:


I would consider doing this in two passes, first sort and group by letter, giving a sequence of A - Z elements, then partition this sequence into columns.

<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>


来源:https://stackoverflow.com/questions/30579085/creating-alphabetical-index-with-fixed-number-of-columns-in-xslt-2-0

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!