【问题标题】:XSLT; finding most frequent element value in a documentXSLT;在文档中查找最常见的元素值
【发布时间】:2009-06-02 11:24:45
【问题描述】:

如果这是一个非常简单的问题,我们深表歉意;我不太使用 XSLT,在网上也找不到太多建议,因为搜索结果有很多污染!

我有一个格式如下的 XML 文档。它的主要目的是通过 XSLT 以几种方式重新格式化,以便以几种不同的格式显示。

<desk>
<drawer>
    <contents>pencils</contents>
    <quantity>2</quantity>
</drawer>
<drawer>
    <contents>pens</contents>
    <quantity>15</quantity>
</drawer>
<drawer>
    <contents>pencils</contents>
    <quantity>3</quantity>
</drawer>
<drawer>
    <contents>rulers</contents>
    <quantity>2</quantity>
</drawer>
</desk>

我想从xml中提取两条信息:i)平均数量; ii) 在 xml 中出现次数最多的内容(即“铅笔”,因为它出现两次,而不是“钢笔”,因为它的数量最多)。这个想法是,这可以通过管道传输到一个非常简单的 shell 脚本中。因此,我认为获取此信息的最简单方法是编写几个简短的 xsl 样式表,然后使用 xsltproc 获取数据。

第一条信息似乎直截了当。样式表的核心是这一行:

<xsl:value-of select="(sum(drawer/quantity)) div (count(drawer))" />

但我有点被第二个卡住了。

我想我可以使用这样的 for 循环遍历每个单独内容的列表:

<xsl:for-each select="drawer[not(contents = preceding-sibling::drawer/contents)]" />

但我不太确定如何计算具有 $current_contents 的元素数量及其内容元素的值。我也看不到按结果排序的简单方法,因此我可以获得最常遇到的内容值的名称。

我觉得这在 XSLT 2.0 中更容易,因为它有各种分组选项,但不幸的是,xsltproc 似乎不支持这一点。任何帮助将不胜感激。

非常感谢,

雅各布

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    与在 XSLT 中解决的许多问题一样,我认为您的答案是 muenchian grouping。按您感兴趣的任何数据分组,一个 for-each 反对将让您使用 xsl:sort 然后对第一个结果执行您需要的任何操作。

    未经测试的、顶级的、可能是更清洁的代码:

    <xsl:key name="average" match="desk/drawer/contents" use="text()"/>
    
    <xsl:template match="/">
        <xsl:for-each select="desk/drawer/contents[generate-id() = generate-id(key('average',text())[1])]">     
            <xsl:sort select="count(//desk/drawer/contents[text()=current()])"  order="descending"/>
            <xsl:if test="position()=1">
                Most common value: "<xsl:value-of select="current()"/>" (<xsl:value-of select="count(//desk/drawer/contents[text()=current()])"/>)
            </xsl:if>       
        </xsl:for-each>
    </xsl:template>
    

    【讨论】:

    • 谢谢;这很有帮助。我不知道该怎么做,尽管是“你需要的任何东西”位。按“内容”对 xml 进行分组后,是否有一种简单的方法可以计算特定“内容”值在 xml 中出现的次数?
    • 非常感谢;这真的很有帮助并且有效!现在我只需要弄清楚它是如何工作的! :)
    • Muenchian 技术对所有可能的类型进行分组,因此您将循环限制为可能的候选者数量(如果您预计重复次数较少或候选者数量较少,这可能会适得其反) . for-each 仅允许您使用仅基于每个不同值的计数的排序。如果只是裁剪输出,遗憾的是没有等效于 XSLT 的中断 :)
    【解决方案2】:

    for-each 中的排序是通过 sort 元素完成的。只需按数量排序并(如果您只想要最频繁的)添加一个&lt;xsl:if test="position()=1"&gt; 标签以仅获取循环中的第一个。

    <xsl:for-each select="drawer">
       <xsl:sort select="quantity" data-type="number" order="descending"/>
       <xsl:if test="position()=1">
          Most frequent: <xsl:value-of select="contents"> with <xsl:value-of select="quantity"> items
       </xsl:if>
    </xsl:for-each>
    

    【讨论】:

    • 啊,对不起,应该更清楚地解释自己。这将产生结果“铅笔”,因为有 17 支铅笔。我想要的是它产生“铅笔”,因为“铅笔”出现两次,“笔”和“尺子”出现一次。
    【解决方案3】:

    已经有一段时间了,但我认为类似的方法可能会奏效。

    先统计所有内容

    <xsl:variable name="tally">
      <xsl:for-each select="drawer">
         <contents count="{count(drawer[contents = current()/contents])}"><xsl:value-of select="contents"/></contents>
      </xsl:for-each>
    </xsl:variable>
    

    请注意,每次都会计算重复的条目,$tally 将包含:

    <contents count="2">pencils</contents>
    <contents count="1">pens</contents>
    <contents count="2">pencils</contents>
    <contents count="1">rulers</contents>
    

    然后用它来找到一个没有其他更高计数的:

    <xsl:variable name="mostfrequentcontents" select="$tally/contents[not($tally/contents/@count > @count)]" />
    

    根据您的 xslt 处理器,您可能必须使用节点集函数将 $tally 转换为节点集。

    【讨论】:

    • 谢谢;我永远无法在 xslt 周围思考这些做事方式!使用 xsltproc,我得到以下信息,所以我认为我需要使用 node-set。我现在正在查阅手册... XPath 错误:无效类型运行时错误:文件 port.xsl 第 11 行元素变量无法评估变量“最频繁内容”的表达式。
    猜你喜欢
    • 2010-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-11
    相关资源
    最近更新 更多