XSL: List divided into columns

2019-02-19 03:58发布

问题:

help me please. There is a list of nodes.

<list>
  <item>1</item>
  <item>2</item>
  <item>3</item>
  <item>4</item>
  <item>5</item>
  <item>6</item>
  <item>7</item>
  and so on...
</list>

Need to divide the list of "n" (arbitrary number) equal parts.

If the number of nodes is not divided equally, then let the last set of nodes will contain the remainder of the division.

For example, if the input list contains 33 elements and the output should have 4 parts with uniformly distributed elements. At the exit to get 3 parts to 9 of elements and one part with 6 elements in the sum of 33.

input

<ul>
    <li>1</li>
    <li>2</li>
    ... 
    <li>33</li>
</ul>

Output

<ul>
    <li>1</li>
    <li>2</li>
    ... 
    <li>9</li>
</ul>
<ul>
    <li>10</li>
    <li>11</li>
    ... 
    <li>18</li>
</ul>
<ul>
    <li>19</li>
    <li>11</li>
    ... 
    <li>27</li>
</ul>
<ul>
    <li>28</li>
    <li>30</li>
    ... 
    <li>33</li>
</ul>

Divided into 4 cols.

回答1:

This solution doesn't require that the nodes to be grouped into columns should be siblings:

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

 <xsl:variable name="vNodes" select="/*/*/text()"/>

 <xsl:param name="vNumParts" select="4"/>

 <xsl:variable name="vNumCols" select=
   "ceiling(count($vNodes) div $vNumParts)"/>

 <xsl:template match="/">
   <table border="1">
     <xsl:for-each select=
        "$vNodes[position() mod $vNumCols = 1]">
       <xsl:variable name="vCurPos" select=
         "(position()-1)*$vNumCols +1"/>
       <tr>
          <xsl:for-each select=
            "$vNodes[position() >= $vCurPos
                    and
                     not(position() > $vCurPos + $vNumCols -1)
                     ]">
           <td><xsl:copy-of select="."/></td>
          </xsl:for-each>
       </tr>
     </xsl:for-each>
   </table>
 </xsl:template>
</xsl:stylesheet>

When applied on this XML document:

<list>
  <item>1</item>
  <item>2</item>
  <item>3</item>
  <item>4</item>
  <item>5</item>
  <item>6</item>
  <item>7</item>
  <item>8</item>
  <item>9</item>
  <item>10</item>
  <item>11</item>
  <item>12</item>
  <item>13</item>
  <item>14</item>
  <item>15</item>
  <item>16</item>
  <item>17</item>
  <item>18</item>
  <item>19</item>
  <item>20</item>
  <item>21</item>
  <item>22</item>
  <item>23</item>
  <item>24</item>
  <item>25</item>
  <item>26</item>
  <item>27</item>
  <item>28</item>
  <item>29</item>
  <item>30</item>
  <item>31</item>
  <item>32</item>
  <item>33</item>
</list>

The wanted result is produced:

<table border="1">
   <tr>
      <td>1</td>
      <td>2</td>
      <td>3</td>
      <td>4</td>
      <td>5</td>
      <td>6</td>
      <td>7</td>
      <td>8</td>
      <td>9</td>
   </tr>
   <tr>
      <td>10</td>
      <td>11</td>
      <td>12</td>
      <td>13</td>
      <td>14</td>
      <td>15</td>
      <td>16</td>
      <td>17</td>
      <td>18</td>
   </tr>
   <tr>
      <td>19</td>
      <td>20</td>
      <td>21</td>
      <td>22</td>
      <td>23</td>
      <td>24</td>
      <td>25</td>
      <td>26</td>
      <td>27</td>
   </tr>
   <tr>
      <td>28</td>
      <td>29</td>
      <td>30</td>
      <td>31</td>
      <td>32</td>
      <td>33</td>
   </tr>
</table>


回答2:

This is a separate answer to a new question asked by the OP in one of his comments to the accepted answer:

Thank you very much, your code is fully working. That right! Then yet another question: how can first sort the whole list alphabetically, and then divide it in columns? – @kalininew

This is almost as easy as before, with one additional step required:

  1. Sort the nodes

  2. Apply the xxx:node-set() extension function (hint: exslt:node-set() is implemented by most browsers) to convert the RTF (Result Tree Fragment) created in step 1. above to a regular node-set.

  3. Apply the transformation that solves the original problem, to the result of step 2. above.



回答3:

<xsl:variable name="max" select="4" />

<xsl:template match="/">
  <xsl:apply-templates select="list" mode="split" />
</xsl:template>

<xsl:template match="list" mode="split">
  <xsl:apply-templates select="item[position() mod $max = 1]" mode="split" />
</xsl:template>

<xsl:template match="item" mode="split">
  <list>
    <xsl:copy-of select=". | following-sibling::item[position() &lt; $max]" />
  </list>
</xsl:template>


标签: xml xslt