我有几个标题,我通过循环一些xml(它可以是n号标题)得到。我希望将它们水平显示在3列中,但是按字母顺序垂直显示:
如果我有3个标题,我只用字母代表它们(我可以得到没有标题的数量。
A B C -----count(3)
4 titles:
A C D -----count(4)
B
5 Titles:
A C E -----count(5)
B D
7 Titles:
A D F -----count(7)
B E G
C
我正在使用xsl 1.0,现在我就像
一样<div class="navigation">
<ul>
<xsl:foreach select="/Custom/Alphabet/titles">
<li>
<xsl:value-of select="." />
</li>
</xsl:foreach>
</ul>
</div>
答案 0 :(得分:4)
很棒的问题!
<强>予。这是一个XSLT 2.0解决方案(65行,几乎可以机械地转换为XSLT 1.0):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="xs my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vItems" select="/*/*/string(.)"
as="item()+"/>
<xsl:template match="/">
<xsl:sequence select="my:fill($vItems, 3)"/>
</xsl:template>
<xsl:function name="my:fill" as="element()+">
<xsl:param name="pItems" as="item()*"/>
<xsl:param name="pK" as="xs:integer"/>
<xsl:variable name="pN" select="count($pItems)"/>
<xsl:choose>
<xsl:when test="$pN le $pK">
<xsl:sequence select="my:fillRow($pItems)"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vColSize"
select="ceiling($pN div $pK)"/>
<xsl:variable name="vCol-1" select=
"$pItems[position() le $vColSize]"/>
<xsl:variable name="vSubTable"
select="my:fill($pItems[position() gt $vColSize],
$pK -1
)
"/>
<xsl:sequence select="my:merge($vCol-1, $vSubTable)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:function name="my:fillRow" as="element()">
<xsl:param name="pItems" as="item()*"/>
<row>
<xsl:for-each select="$pItems">
<cell><xsl:sequence select="."/></cell>
</xsl:for-each>
</row>
</xsl:function>
<xsl:function name="my:merge" as="element()*">
<xsl:param name="pCol" as="item()*"/>
<xsl:param name="pTable" as="element()*"/>
<xsl:for-each select="$pCol">
<xsl:variable name="vrowPos" select="position()"/>
<row>
<cell><xsl:sequence select="."/></cell>
<xsl:sequence select="$pTable[position() eq $vrowPos]/cell"/>
</row>
</xsl:for-each>
</xsl:function>
</xsl:stylesheet>
将此转换应用于所提供的(最复杂的)7项案例:
<titles>
<t>A</t>
<t>B</t>
<t>C</t>
<t>D</t>
<t>E</t>
<t>F</t>
<t>G</t>
</titles>
产生了想要的正确结果:
<row>
<cell>A</cell>
<cell>D</cell>
<cell>F</cell>
</row>
<row>
<cell>B</cell>
<cell>E</cell>
<cell>G</cell>
</row>
<row>
<cell>C</cell>
</row>
我已经确认每个 N = 1 to 7
都会产生预期的正确结果。
说明:
我们正在构建输入序列(pN
)中项目数的递归所需的表:
递归的 base 适用于任何$pN
不大于$pK
(所需列数)。在这个基本情况下,表格只有一行。
在一般情况下$pN > $pK
;然后我们构建最左边的列$vCol-1
,并递归地创建一个较小的表,其中包含其余项和新的所需列数:$pK -1
。
在上面的情况2.我们最终合并了列和子表以生成结果表。
<强> II。等效的XSLT 2.0解决方案,以“更多XSLT 2.0风格”(60行)编写:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="xs my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vItems" select="/*/*/string(.)"
as="item()+"/>
<xsl:template match="/">
<xsl:sequence select="my:fill($vItems, 3)"/>
</xsl:template>
<xsl:function name="my:fill" as="element()+">
<xsl:param name="pItems" as="item()*"/>
<xsl:param name="pK" as="xs:integer"/>
<xsl:sequence select=
"for $vN in count($pItems)
return
if($vN le $pK)
then my:fillRow($pItems)
else
(for $vColSize in xs:integer(ceiling($vN div $pK))
return
my:merge((for $i in 1 to $vColSize
return $pItems[$i]),
my:fill((for $i in $vColSize+1 to $vN
return $pItems[$i]),
$pK -1
)
)
)
"/>
</xsl:function>
<xsl:function name="my:fillRow" as="element()">
<xsl:param name="pItems" as="item()*"/>
<row>
<xsl:for-each select="$pItems">
<cell><xsl:sequence select="."/></cell>
</xsl:for-each>
</row>
</xsl:function>
<xsl:function name="my:merge" as="element()*">
<xsl:param name="pCol" as="item()*"/>
<xsl:param name="pTable" as="element()*"/>
<xsl:for-each select="$pCol">
<xsl:variable name="vrowPos" select="position()"/>
<row>
<cell><xsl:sequence select="."/></cell>
<xsl:sequence select="$pTable[position() eq $vrowPos]/cell"/>
</row>
</xsl:for-each>
</xsl:function>
</xsl:stylesheet>
<强> III。 XSLT 1.0解决方案(75行)
这是第一个XSLT 2.0解决方案(上图),几乎机械地转换为XSLT 1.0:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common" exclude-result-prefixes="ext">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vItems" select="/*/*"/>
<xsl:template match="/">
<xsl:call-template name="fill">
<xsl:with-param name="pItems" select="$vItems"/>
<xsl:with-param name="pK" select="3"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="fill">
<xsl:param name="pItems"/>
<xsl:param name="pK"/>
<xsl:variable name="vN" select="count($pItems)"/>
<xsl:choose>
<xsl:when test="not($vN > $pK)">
<row>
<xsl:call-template name="fillRow">
<xsl:with-param name="pItems" select="$pItems"/>
</xsl:call-template>
</row>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vColSize"
select="ceiling($vN div $pK)"/>
<xsl:variable name="vCol-1" select=
"$pItems[not(position() > $vColSize)]"/>
<xsl:variable name="vrtfSubtable">
<xsl:call-template name="fill">
<xsl:with-param name="pItems" select=
"$pItems[position() > $vColSize]"/>
<xsl:with-param name="pK" select="$pK -1"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vSubTable" select=
"ext:node-set($vrtfSubtable)/*"/>
<xsl:call-template name="merge">
<xsl:with-param name="pCol" select="$vCol-1"/>
<xsl:with-param name="pTable" select="$vSubTable"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="fillRow">
<xsl:param name="pItems"/>
<xsl:for-each select="$pItems">
<cell><xsl:value-of select="."/></cell>
</xsl:for-each>
</xsl:template>
<xsl:template name="merge">
<xsl:param name="pCol"/>
<xsl:param name="pTable"/>
<xsl:for-each select="$pCol">
<xsl:variable name="vrowPos" select="position()"/>
<row>
<cell><xsl:value-of select="."/></cell>
<xsl:copy-of select="$pTable[position() = $vrowPos]/cell"/>
</row>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
<强> IV。最后,一个纯粹的,生成性的(非递归的)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="pK" select="3"/>
<xsl:variable name="vItems" select="/*/*"/>
<xsl:template match="/">
<xsl:call-template name="genTable"/>
</xsl:template>
<xsl:template name="genTable">
<xsl:param name="pItems" select="$vItems"/>
<xsl:param name="pK" select="$pK"/>
<xsl:variable name="vN" select=
"count($vItems)"/>
<xsl:variable name="vnumRows"
select="ceiling($vN div $pK)"/>
<table>
<xsl:for-each select=
"$pItems[not(position() > $vnumRows)]">
<xsl:call-template name="genRow">
<xsl:with-param name="pRowInd" select="position()"/>
<xsl:with-param name="pItems" select="$vItems"/>
<xsl:with-param name="pK" select="$pK"/>
</xsl:call-template>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template name="genRow">
<xsl:param name="pRowInd" select="position()"/>
<xsl:param name="pItems" select="$vItems"/>
<xsl:param name="pK" select="$pK"/>
<xsl:variable name="vN" select=
"count($vItems)"/>
<xsl:variable name="vFullCols" select=
"$vN mod $pK"/>
<xsl:variable name="vFullColSize" select=
"ceiling($vN div $pK)"/>
<tr>
<td><xsl:value-of select="$pItems[number($pRowInd)]"/></td>
<xsl:for-each select=
"$pItems[position() > 1
and
not(position() > $pK)
]">
<xsl:variable name="vX" select="position()+1"/>
<xsl:variable name="vMinFullColsAndX" select=
"($vX > $vFullCols) * $vFullCols
+
not($vX > $vFullCols) * $vX
"/>
<xsl:variable name="vAmmt1" select=
"$vMinFullColsAndX * $vFullColSize
"/>
<xsl:variable name="vAmmt2" select=
"($vX -1 - $vMinFullColsAndX) * ($vFullColSize -1)
"/>
<xsl:variable name="vValue" select=
"$vAmmt1 + $vAmmt2"/>
<xsl:if test="not(($pRowInd -1) * $pK +$vX > $vN)">
<td><xsl:value-of select=
"$pItems[position()=$pRowInd+$vValue]"/>
</td>
</xsl:if>
</xsl:for-each>
</tr>
</xsl:template>
</xsl:stylesheet>
应用于同一XML文档(上图)时,会生成所需的正确结果:
<table>
<tr>
<td>A</td>
<td>D</td>
<td>F</td>
</tr>
<tr>
<td>B</td>
<td>E</td>
<td>G</td>
</tr>
<tr>
<td>C</td>
</tr>
</table>
答案 1 :(得分:1)
CSS3实际上定义了一个漂亮的属性,为您执行此操作,称为column-count
。在良好的浏览器和IE10中它是supported。如果向后兼容性不是问题,这是一个非常好的解决方案,接近你想要的。