XSLT; найти наиболее частое значение элемента в документе - PullRequest
1 голос
/ 02 июня 2009

Извинения, если это очень простой вопрос; Я не очень часто использую 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 (то есть «карандаши», потому что оно появляется дважды, а не «ручки», потому что оно имеет наибольшее количество). Идея состоит в том, что это может быть передано в очень простой сценарий оболочки. Поэтому я подумал, что самый простой способ получить эту информацию - написать пару коротких таблиц стилей xsl, а затем использовать xsltproc для получения данных.

Первая часть информации кажется простой. Сердцем таблицы стилей будет эта строка:

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

но я немного застрял на втором.

Я думаю, что могу использовать что-то вроде этого для цикла по списку каждого отдельного контента:

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

но я не совсем уверен, как тогда подсчитать количество элементов, которые имеют $ current_contents и значение их элемента содержимого. Также я не вижу простого способа сортировки по результатам, чтобы получить имя наиболее часто встречающегося значения содержимого.

У меня такое ощущение, что это проще в XSLT 2.0 с его различными опциями группировки, но, к сожалению, xsltproc, похоже, этого не поддерживает. Любая помощь будет принята с благодарностью.

Большое спасибо,

Jacob

Ответы [ 3 ]

2 голосов
/ 02 июня 2009

Как и в случае множества проблем, решаемых в XSLT, я думаю, что ваш ответ здесь muenchian grouping . Группируйте по интересующим вас данным, для каждого из них вы сможете использовать 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>
0 голосов
/ 02 июня 2009

Это было давно, но я думаю, что-то в этом роде может сработать.

Сначала посчитать все содержимое

<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 в набор узлов с помощью функции набора узлов.

0 голосов
/ 02 июня 2009

Сортировка в for-each осуществляется через элемент sort. Просто отсортируйте по количеству и (если вы хотите только наиболее часто) добавьте тег <xsl:if test="position()=1">, чтобы получить только первое в цикле.

<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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...