XSL: подсчет предыдущих уникальных одноуровневых элементов

попытаться инкапсулировать генерацию старшей диаграммы в событии просмотра страницы

$(document).on("pageshow", "#hg_graph", function() {...});
5
задан Jay 2 June 2009 в 20:34
поделиться

3 ответа

Эту проблему довольно легко решить с помощью XPath. Вот выражение, которое вы ищете: count ((. | Previous-sibling :: ROLE) [not (@name = previous-sibling :: ROLE / @ name)])

Это можно разбить чтобы сделать его более читабельным, как я сделал в следующей таблице стилей XSLT 1.0:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:output method="text"/>

  <!-- don't copy whitespace -->
  <xsl:template match="text()"/>

  <xsl:template match="ROLE">
    <xsl:variable name="roles-so-far" select=". | preceding-sibling::ROLE"/>
    <!-- Only select the first instance of each ROLE name -->
    <xsl:variable name="roles-so-far-unique"
                  select="$roles-so-far[not(@name = preceding-sibling::ROLE/@name)]"/>
    <xsl:apply-templates select="@name"/>
    <xsl:text> </xsl:text>
    <xsl:value-of select="count($roles-so-far-unique)"/>
    <xsl:text>&#xA;</xsl:text> <!-- linefeed -->
  </xsl:template>

</xsl:stylesheet>

Вот альтернативная реализация, использующая метод Мюнчи. Сначала объявите ключ:

<xsl:key name="roles" match="ROLE" use="@name"/>

Затем замените определение $ role-so-far-unique на что-то вроде этого:

<!-- Among all the ROLEs having one of the names so far,
     select only the first one for each name -->
<xsl:variable name="roles-so-far-unique"
              select="../ROLE[@name = $roles-so-far/@name]
                             [generate-id(.) = generate-id(key('roles',@name)[1])]"/>

Этот код, конечно, более сложный. Если у вас нет большого набора данных, требующего от вас ускорения обработки с использованием метода Мюнчи (даже в этом случае я бы проверил, что он вам что-нибудь пригодится), вы также можете придерживаться более простой версии, приведенной выше.

Наконец, в XSLT 2.0, это намного проще.

5
ответ дан 14 December 2019 в 04:46
поделиться

Рекурсия обычно хорошо работает с такими проблемами.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="text" media-type="text/plain" />

    <xsl:template name="count-previous-but-not-with-my-name">
        <xsl:param name="nodes" />
        <xsl:param name="count" select="0" />
        <xsl:choose>
            <xsl:when test="count($nodes) = 0">
                <xsl:value-of select="$count" />
            </xsl:when>
            <xsl:otherwise>
                <xsl:variable name="last-name" select="$nodes[last()]/@name" />
                <xsl:variable name="nodes-before-me-without-my-name" select="$nodes[position() &lt; last() and @name != $last-name]" />
                <xsl:call-template name="count-previous-but-not-with-my-name">
                    <xsl:with-param name="nodes" select="$nodes-before-me-without-my-name" />
                    <xsl:with-param name="count" select="$count + 1" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="/">
        <xsl:for-each select="//ROLEACTIONINFO/ROLE">
            <xsl:variable name="role" select="current()" />
            <xsl:variable name="my-pos" select="position()" />
            <xsl:value-of select="current()/@name" /><xsl:text> </xsl:text>
            <xsl:call-template name="count-previous-but-not-with-my-name">
                <xsl:with-param name="nodes" select="$role/../ROLE[position() &lt;= $my-pos]" />
            </xsl:call-template>
            <xsl:text>&#10;</xsl:text>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>
0
ответ дан 14 December 2019 в 04:46
поделиться

Это легко решается с помощью :

<xsl:stylesheet
  version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>

  <xsl:output method="text" />

  <xsl:key name="kRole" match="ROLE" use="@name" />

  <xsl:template match="ROLE">
    <xsl:value-of select="concat(@name, ' ')" />
    <xsl:value-of select="count(
      (. | preceding-sibling::ROLE)[
        count(. | key('kRole', @name)[1]) = 1
      ])" />
  </xsl:template>

</xsl:stylesheet>

Результат такой, как нужно:

TESTER 1
PARENT1 2
PARENT1 2
PARENT1 2
PARENT2 3
PARENT2 3
PARENT3 4
PARENT4 5
TESTROLE 6

Объяснение выражения XPath в :

count(                          # count the nodes:
(. | preceding-sibling::ROLE)   # union of this node and its predecessors
[                               # where...
  count(                        # the count of the union of...
    . |                         #   this node and
    key('kRole', @name)[1]      #   the first node with the same @name
  ) = 1                         # is 1
]
)

Это метод Мюнчи. Основываясь на том факте, что набор узлов не может содержать один и тот же узел дважды, объединение двух узлов имеет счетчик узлов 1, если они являются одним и тем же узлом. Таким образом, мы выбираем уникальные узлы только из (. | Previous-sibling :: ROLE) .

Если в вашем документе более одного элемента , отсутствует родительская проверка. Этого также легко достичь:

  <xsl:template match="ROLE">
    <xsl:variable name="parentId" select="generate-id(..)" />
    <xsl:value-of select="count(
      (. | preceding-sibling::ROLE)[
        count(. | key('kRole', @name)[generate-id(..) = $parentId][1]) = 1
      ])" />
  </xsl:template>

Обратите внимание, что [generate-id (..) = $ parentId] [1] ! = [1] [generate-id (..) = $ parentId] .

Порядок важен при связывании предикатов. Первый сначала проверяет равенство родительских узлов, а затем берет первый уникальный узел из сокращенного набора. Это то, что мы хотим.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

ROLE) только .

Если в вашем документе более одного элемента , значит родительская проверка отсутствует. Этого также легко достичь:

  <xsl:template match="ROLE">
    <xsl:variable name="parentId" select="generate-id(..)" />
    <xsl:value-of select="count(
      (. | preceding-sibling::ROLE)[
        count(. | key('kRole', @name)[generate-id(..) = $parentId][1]) = 1
      ])" />
  </xsl:template>

Обратите внимание, что [generate-id (..) = $ parentId] [1] ! = [1] [generate-id (..) = $ parentId] .

Порядок важен при связывании предикатов. Первый сначала проверяет равенство родительских узлов, а затем берет первый уникальный узел из сокращенного набора. Это то, что мы хотим.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

ROLE) только .

Если в вашем документе более одного элемента , значит родительская проверка отсутствует. Этого также легко достичь:

  <xsl:template match="ROLE">
    <xsl:variable name="parentId" select="generate-id(..)" />
    <xsl:value-of select="count(
      (. | preceding-sibling::ROLE)[
        count(. | key('kRole', @name)[generate-id(..) = $parentId][1]) = 1
      ])" />
  </xsl:template>

Обратите внимание, что [generate-id (..) = $ parentId] [1] ! = [1] [generate-id (..) = $ parentId] .

Порядок важен при связывании предикатов. Первый сначала проверяет равенство родительских узлов, а затем берет первый уникальный узел из сокращенного набора. Это то, что мы хотим.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

Этого также легко достичь:

  <xsl:template match="ROLE">
    <xsl:variable name="parentId" select="generate-id(..)" />
    <xsl:value-of select="count(
      (. | preceding-sibling::ROLE)[
        count(. | key('kRole', @name)[generate-id(..) = $parentId][1]) = 1
      ])" />
  </xsl:template>

Обратите внимание, что [generate-id (..) = $ parentId] [1] ! = [1] [generate-id (..) = $ parentId] .

Порядок важен при связывании предикатов. Первый сначала проверяет равенство родительских узлов, а затем берет первый уникальный узел из сокращенного набора. Это то, что мы хотим.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

Этого также легко достичь:

  <xsl:template match="ROLE">
    <xsl:variable name="parentId" select="generate-id(..)" />
    <xsl:value-of select="count(
      (. | preceding-sibling::ROLE)[
        count(. | key('kRole', @name)[generate-id(..) = $parentId][1]) = 1
      ])" />
  </xsl:template>

Обратите внимание, что [generate-id (..) = $ parentId] [1] ! = [1] [generate-id (..) = $ parentId] .

Порядок важен при связывании предикатов. Первый сначала проверяет равенство родительских узлов, а затем берет первый уникальный узел из сокращенного набора. Это то, что мы хотим.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

Последний берет первый узел из набора (все узлы ROLE с заданным именем по всему документу), берет первый и затем сохраняет или отбрасывает его на основе родительского равенства. Это неправильно.

3
ответ дан 14 December 2019 в 04:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: