Принятая в настоящее время ответ имеет O (N^2) временную сложность, так как он использует preceding-sibling::*
для каждого элемента ,
Вот решение, которое может быть более эффективным - нет preceding-sibling::*
оси используется:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:variable name="vTop" select="."/>
<xsl:variable name="vNames" select="distinct-values(*/name())"/>
<xsl:variable name="vCountNames" select="count($vNames)"/>
<xsl:for-each select="1 to $vCountNames">
<xsl:variable name="vCol" select="position()"/>
<xsl:for-each select="$vNames">
<xsl:apply-templates select="$vTop/*[name() eq current()][$vCol]"/>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Когда это преобразование применяется на следующий документ XML (Предоставленный фрагмент, окруженный верхней (документ) элемент):
<t>
<a>t1</a> <a>t2</a>
<b>t3</b> <b>t4</b> <b>t5</b>
<c>t6</c>
</t>
Требуемая результат получается (значения каждого е lement при проходимом столбцах):
t1t3t6t2t4t5
Это решение O(N * M)
, где N есть число элементов, и М представляет собой число их различных названий.
Таким образом, если N = k times M
, то это решение будет асимптотически k
раз быстрее, чем решение O(N^2)
.
II. Один, чистый XPath 2.0 выражения посещения элементы в столбцам образом:
for $vTop in /*,
$vCol in 1 to count(distinct-values($vTop/*/name())),
$vName in distinct-values($vTop/*/name())
return
$vTop/*[name() eq $vName][$vCol]
XSLT на основе проверки:
<xsl:template match="/*">
<xsl:sequence select=
"for $vTop in /*,
$vCol in 1 to count(distinct-values($vTop/*/name())),
$vName in distinct-values($vTop/*/name())
return
$vTop/*[name() eq $vName][$vCol]
"/>
</xsl:template>
</xsl:stylesheet>
При нанесении на том же самом документе XML , это преобразование оценивает выражение XPath и выводит результат этой оценки:
t1t3t6t2t4t5
III. XSLT 1.0 решение:
Это преобразование:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kByName" match="/*/*" use="name()"/>
<xsl:variable name="vDistinctNamed" select=
"/*/*[generate-id() = generate-id(key('kByName', name())[1])]"/>
<xsl:variable name="vNumCols">
<xsl:for-each select="/*/*[generate-id() = generate-id(key('kByName', name())[1])]">
<xsl:sort select=
"count(key('kByName', name()))" data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of select="count(key('kByName', name()))"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:template match="/*">
<xsl:for-each select="*[not(position() > $vNumCols)]">
<xsl:variable name="vCol" select="position()"/>
<xsl:for-each select="$vDistinctNamed">
<xsl:variable name="vthisElement" select="/*/*[name() = name(current())][$vCol]"/>
<xsl:if test="$vthisElement">
<xsl:value-of select="concat(/*/*[name() = name(current())][$vCol],', ')"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
при нанесении на том же самом документе XML, производит тот же самый правильный результат:
t1, t3, t6, t2, t4, t5,
Это неясно. Между первым и вторым блоками нет семантической разницы. Что вы подразумеваете под «группой»? Вам нужно показать _output_, который вы хотите создать из ввода. Просто вставка новых строк после каждой строки тегов с тем же именем не является значимым преобразованием. –
Я, с надеждой, уточнил описание вопроса. – dave