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>
Пояснение :
Вложенный xsl:for-each-group
- сначала сгруппированный по начальному символу, затем для каждой такой определенной и отсортированной группы - по номеру столбца, в которомдолжны быть следующие элементы:
Использование стандартных функций 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>
Объяснение :
Использование метод группирования по Мюнхену плюс сортировка, мы получаем (в отсортированном порядке) каждую группу name
элементов, состоящих из всех имен, начинающихся с одного и того же символа.
Каждая группа элементов name
, полученная выше, обрабатывается путем применения шаблонов к ее первому элементу name
.Вся группа, ее длина и индекс элемента name
в группе (по умолчанию = 1) передаются в качестве параметров.
Шаблон, соответствующий 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)
]"/>