2

i have a xml doc like:

<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>

and I want to count all names which are the same: Tim 2, Jack 1, Jones 1 and it should give an output like:

<x> Tim </x> 

because TIM is the highest name

I hope you can help me... (sorry for my bad english)

2
  • Good question, +1. See my answer for two complete, short and easy solutions (XPath 2.0/XQuery and XSLT 1.0). Commented May 8, 2011 at 18:10
  • As one can see, using an XPath expression has a better portability and can be used unchanged both in XQuery and XSLT 2.0, and in any language hosting XPath 2.0. Commented May 8, 2011 at 19:53

3 Answers 3

5

In XPath 2.0, XSLT 2.0 and XQuery use (exactly the same solution):

(/*/*/*/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]

You can also get this element easily with the following XSLT 1.0 transformation:

<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>

When the above XPath 2.0/XQuery expression or XSLT transformation are evaluated (applied) on the provided XML document:

<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>

the correct element is selected (produced):

<names>Tim</names>
Sign up to request clarification or add additional context in comments.

Comments

1
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]

1 Comment

This loads a file: let $xml := doc("data.xml")
1

The solution of Gunther is the best, and if you wanted to count every elements you could do:

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])))

With result Tim - 2 Jack - 1 Johons - 1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.