Как посчитать элементы с одинаковыми именами? XML -> Xquery - PullRequest
2 голосов
/ 08 мая 2011

У меня есть XML-документ, как:

<root>
<test>
    <humans>
        <names>Tim</names>
    </humans>
</test>
<test>
    <humans>
        <names>Jack</names>
        <names>Jones</names>
    </humans>
</test>
<test>
    <humans>
        <names>Tim</names>
    </humans>
</test>
</root>

и я хочу сосчитать все одинаковые имена: Тим 2, Джек 1, Джонс 1 и это должно дать вывод как:

<x> Tim </x> 

потому что TIM - высшее имя

Надеюсь, вы мне поможете ... (извините за мой плохой английский)

Ответы [ 3 ]

5 голосов
/ 08 мая 2011

В XPath 2.0, XSLT 2.0 и XQuery используют (точно такое же решение):

(/*/*/*/names[for $v in .,
                    $cnt in count(/*/*/*/names[. eq $v])
                 return
                    $cnt
                   eq
                     max(for $n in distinct-values(/*/*/*/names)
                           return
                              count(/*/*/*/names[. eq $n])
                        )
                ]
    )[1]

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

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:key name="kNamesByVal" match="names" use="."/>

 <xsl:template match="/">
  <xsl:for-each select=
   "*/*/*/names[generate-id()
               =
                generate-id(key('kNamesByVal',.)[1])
               ]">
   <xsl:sort select="count(key('kNamesByVal',.))"
    data-type="number" order="descending"/>

    <xsl:if test="position()=1">
      <xsl:copy-of select="."/>
    </xsl:if>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

Когда вышеуказанное выражение XPath 2.0 / XQuery или преобразование XSLT оцениваются (применяются) в предоставленном XML-документе :

<root>
    <test>
        <humans>
            <names>Tim</names>
        </humans>
    </test>
    <test>
        <humans>
            <names>Jack</names>
            <names>Jones</names>
        </humans>
    </test>
    <test>
        <humans>
            <names>Tim</names>
        </humans>
    </test>
</root>

выбран правильный элемент (произведен):

<names>Tim</names>
1 голос
/ 13 мая 2011

Лучше всего решение Гюнтера, и если вы хотите сосчитать все элементы, которые вы можете сделать:

xquery version "1.0";

for $x in
(
  for $name in distinct-values(//names)
  order by count(//names[. = $name]) descending
  return <x>{$name}</x>
) return fn:concat($x, ' - ',xs:string(count(//names[. = $x])))

С результатом Тим - 2 Джека - 1 Джонс - 1

1 голос
/ 08 мая 2011
let $xml := <!-- your xml document -->
return
(
  for $name in distinct-values($xml//names)
  order by count($xml//names[. = $name]) descending
  return <x>{$name}</x>
)[1]
...