ManiK
ManiK

Reputation: 397

XPath - how to count() & group by each element value?

Sample XML document:

<?xml version="1.0"?>
<names>
  <name>abc</name>
  <name>abc</name>
  <name>xyz</name>
  <name>def</name>
  <name>ghi</name>
</names>

Output needed as:

abc: 2
xyz: 1
def: 1
ghi: 1

Tried the below XPath expression :-

for $n in //names/name return concat($n, ': ', count(//$n))

But output comes like this:

abc: 1
abc: 1
xyz: 1
def: 1
ghi: 1

Upvotes: 3

Views: 962

Answers (2)

Dimitre Novatchev
Dimitre Novatchev

Reputation: 243529

A shorter and simpler XPath 2.0 solution (no distinct-values(), no concat()):

for $v in /*/*[index-of(/*/*, .)[1]]/node()
  return ($v, ':', count(index-of(/*/*, $v)), '&#xA;')

XSLT - based verification:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

  <xsl:template match="/">
    <xsl:sequence select=
    "for $v in /*/*[index-of(/*/*, .)[1]]/node()
      return ($v, ':', count(index-of(/*/*, $v)), '&#xA;')"/>
  </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the provided XML document:

<names>
  <name>abc</name>
  <name>abc</name>
  <name>xyz</name>
  <name>def</name>
  <name>ghi</name>
</names>

The XPath expression is evaluated and the result of this evaluation is copied to the output:

abc:  2 
xyz:  1 
def:  1 
ghi:  1 

Upvotes: 0

Martin Honnen
Martin Honnen

Reputation: 167706

XPath 3.1 would be

map:merge(
  /names/name!map{ string() : .}, 
  map { 'duplicates': 'combine'}) 
=> 
map:for-each(function($k, $v) {
  $k || ': ' || count($v)
})

In XPath 2:

for $distinct-name in distinct-values(/names/name)
return concat($distinct-name, ': ', count(/names/name[. = $distinct-name]))

Upvotes: 3

Related Questions