如何按照列排列多个相邻元素'在XSLT中?

时间:2016-12-09 17:10:42

标签: xml xslt-2.0 xpath-2.0

假设有这个XML片段:

<a>t1</a> <a>t2</a> <b>t3</b> <b>t4</b> <b>t5</b> <c>t6</c>

我们可以把它想象成:

<a>t1</a> <a>t2</a>
<b>t3</b> <b>t4</b> <b>t5</b>
<c>t6</c>

我想迭代,以便在第一遍中迭代的元素是:

<a>t1</a> <b>t3</b> <c>t6</c>
第二遍

<a>t2</a> <b>t4</b>

和第三:

<b>t5</b>

上述数据只是一个例子。可以有更长的相邻兄弟序列,而不仅仅是这组固定的数据。

要求是每个组包含共享相同元素名称的前一个兄弟姐妹的相同数量的元素。

例如,在第一列&#39; &lt; a /&gt;,&lt; b /&gt;和&lt; c /&gt;分别没有同名的兄弟姐妹。

第二栏&#39; &lt; a /&gt;和&lt; b /&gt;在兄弟计数之前分别具有相同的名称。

我希望能够在for-each-group语句中以这种方式迭代这些项目,但我不确定如何表达group-by子句。

2 个答案:

答案 0 :(得分:2)

模板

# Creating id for each participant
class_survey$id <- rownames(class_survey)
# Reoder the columns in the data frame so that ID comes first
class_survey <- class_survey[c(44, 1:43)]
# Rename the variables using dplyr
# new name = old name
class_survey <- class_survey %>%
  rename(children = Q19_1, gender = Q26, color = Q27)

变换

<xsl:template match="div">
    <xsl:for-each-group select="*" group-by="count(preceding-sibling::*[node-name(.) = node-name(current())])">
        <group key="{current-grouping-key()}">
            <xsl:copy-of select="current-group()"/>
        </group>
    </xsl:for-each-group>
</xsl:template>

<div>
    <a>t1</a> <a>t2</a> <b>t3</b> <b>t4</b> <b>t5</b> <c>t6</c>
</div>

答案 1 :(得分:0)

当前接受的答案具有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>

生成所需结果(列遍历时每个元素的值):

t1t3t6t2t4t5

此解决方案为O(N * M),其中N是元素的数量,M是其不同名称的数量。

因此,如果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,