2008-09-30 18 views
10

¿Cómo contar valores distintos en un nodo en XSLT?¿Cómo contar valores distintos en un nodo?

Ejemplo: Quiero contar el número de países existentes en los nodos del campo, en este caso, sería 3.

<Artists_by_Countries> 
    <Artist_by_Country> 
     <Location_ID>62</Location_ID> 
     <Artist_ID>212</Artist_ID> 
     <Country>Argentina</Country> 
    </Artist_by_Country> 
    <Artist_by_Country> 
     <Location_ID>4</Location_ID> 
     <Artist_ID>108</Artist_ID> 
     <Country>Australia</Country> 
    </Artist_by_Country> 
    <Artist_by_Country> 
     <Location_ID>4</Location_ID> 
     <Artist_ID>111</Artist_ID> 
     <Country>Australia</Country> 
    </Artist_by_Country> 
    <Artist_by_Country> 
     <Location_ID>12</Location_ID> 
     <Artist_ID>78</Artist_ID> 
     <Country>Germany</Country> 
    </Artist_by_Country> 
</Artists_by_Countries> 

Respuesta

24

Si usted tiene un documento de gran tamaño, es probable que desee utilizar el "Método Muenchian", que por lo general se utiliza para agrupar, para identificar los nodos distintos. Declarar una clave que los índices de las cosas que desea contar con los valores que son distintos:

<xsl:key name="artists-by-country" match="Artist_by_Country" use="Country" /> 

entonces se puede obtener los <Artist_by_Country> elementos que tienen países distintos usando:

/Artists_by_Countries 
    /Artist_by_Country 
    [generate-id(.) = 
    generate-id(key('artists-by-country', Country)[1])] 

y puedes contar envolviendo eso en una llamada a la función count().

Por supuesto, en XSLT 2.0, es tan simple como

count(distinct-values(/Artists_by_Countries/Artist_by_Country/Country)) 
+0

Mis ojos estaban vidriosos sobre el grupo superior de líneas y luego encontré la pepita al final –

3

intentar algo como esto:

count(//Country[not(following::Country/text() = text())]) 

"Dame el recuento de todos los nodos País sin un País siguiente con el texto coincidente "

El bit interesante de esa expresión, IMO, es el eje following.

Probablemente se podría también quitar el primer /text(), y vuelva a colocar el segundo con .

+0

Esto sólo funcionará si los nodos se clasifican y los valores país como son, por lo tanto consecutivo. – dacracot

+3

No, siempre funcionará. following :: funciona en todo el documento, si hay CUALQUIER país después del contexto que tenga el mismo valor, ese nodo no se contará. –

+0

Esta debería ser la respuesta aceptada, aunque la opción 2.0 es ideal para las personas que pueden usarla. – Moss

6

En XSLT 1.0 esto no es obvio, pero lo siguiente que debe dar una idea de las necesidades:

count(//Artist_by_Country[not(Location_ID=preceding-sibling::Artist_by_Country/Location_ID)]/Location_ID) 

Cuantos más elementos haya en su XML, más tiempo llevará esto, ya que comprueba cada uno de los hermanos anteriores de cada elemento.

+0

No estoy seguro del rendimiento, pero para XSLT 1.0, esto parece una solución más limpia que la que requiere el elemento en la solución más votado. –

+0

Normalmente hay al menos dos formas de desollar a un gato en XSLT, y lo mejor dependerá de sus circunstancias particulares. xsl: key puede ser muy rápido en un buen procesador en documentos grandes en comparación con mi método anterior, sospecho. – samjudson

0

Si tiene control de la generación xml en la primera aparición de un país, puede agregar un atributo al nodo país como distinct = 'true' marcar el país como "used" y no agregar posteriormente el atributo distinct si vuelves a cruzar ese país.

A continuación, podría hacer

<xsl:for-each select="Artists_by_Countries/Artist_by_Country/Country[@distinct='true']" /> 
Cuestiones relacionadas