2010-09-23 6 views
2

J'ai un fichier XML qui ressemble à ce qui suit ...XSLT: Regroupement et tri .... comment?

<states> 
<state> 
    <name>North Carolina</name> 
    <city>Charlotte</city> 
</state> 
<state> 
    <name>Alaska</name> 
    <city>Fairbanks</city> 
</state> 
<state> 
    <name>Virginia</name> 
    <city>Leesburg</city> 
</state> 
<state> 
    <name>Alaska</name> 
    <city>Coldfoot</city> 
</state> 
<state> 
    <name>North Carolina</name> 
    <city>Harrisburg</city> 
</state> 
<state> 
    <name>Virginia</name> 
    <city>Ashburn</city> 
</state> 
</states> 

Je dois produire un rapport qui répertorie chaque état, est l'ordre alphabétique avec chaque ville suivante .... tels que ..

Alaska - Fairbanks, Coldfoot 
North Carolina - Charlotte, Harrisburg 
Virginia - Leesburg, Ashburn 

(les villes ne doivent pas être en ordre alphabétique, seulement les états)

J'ai essayé de résoudre ce problème en faisant un for-each sur les états/état, le tri par nom et son traitement. Comme cela ....

<xsl:for-each select="states/state"> 
     <xsl:sort select="name" data-type="text" order="ascending"/> 
     <xsl:value-of select="name"/>-<xsl:value-of select="city"/> 
    </xsl:for-each> 

Cela m'a donné ....

Alaska - Fairbanks 
Alaska - Coldfoot 
North Carolina - Charlotte 
North Carolina - Harrisburg 
Virginia - Leesburg 
Virginia - Ashburn 

Le tri a travaillé, maintenant je veux groupe. La seule chose que je pouvais penser à faire était de comparer à l'état précédent, puisqu'il est trié, il devrait reconnaître si la valeur d'état n'a pas changé. Comme ça ...

<xsl:for-each select="states/state"> 
      <xsl:sort select="name" data-type="text" order="ascending"/> 
    <xsl:variable name="name"><xsl:value-of select="name"> 
    <xsl:variable name="previous-name"><xsl:value-of select="(preceding-sibling::state)/name"> 
    <xsl:if test="$name != $previous-name"> 
    <br/><xsl:value-of select="name"/>- 
    </xsl:if> 
    <xsl:value-of select="city"/> 
</xsl:for-each> 

Malheureusement, il semble que la fonction preceding-sibling ne fonctionne pas bien avec le genre, donc, la première fois à (la première de l'Alaska), il a vu la première Caroline du Nord comme frère précédent. Cela provoque des résultats étranges, ce qui n'était pas du tout à mon goût. Donc, j'utilise XSLT1.0 ... Des idées/suggestions?

Merci

Répondre

1

pour le regroupement dans XSLT 1.0 vous allez probablement devoir utiliser le Muenchian Method. Cela peut être difficile à comprendre, mais une fois que vous le faites fonctionner, vous devriez être prêt à partir.

+0

commence à lire à ce sujet .... s'est perdu rapidement. Je vais essayer à nouveau. – Doug

+0

Ouais, je ne vais pas mentir, vous avez du travail à faire pour vous. Je l'ai seulement mis en application quelques fois mais je lutte toujours ... –

4

Cette feuille de style:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:key name="kStateByName" match="state" use="name"/> 
    <xsl:output method="text"/> 
    <xsl:template match="/"> 
     <xsl:apply-templates 
        select="/*/state[count(.|key('kStateByName',name)[1])=1]"> 
      <xsl:sort select="name"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="state"> 
     <xsl:value-of select="concat(name,' - ')"/> 
     <xsl:apply-templates select="key('kStateByName',name)/city"/> 
    </xsl:template> 
    <xsl:template match="city"> 
     <xsl:value-of select="concat(.,substring(', ', 
               1 div (position()!=last())), 
             substring('&#xA;', 
               1 div (position()=last())))"/> 
    </xsl:template> 
</xsl:stylesheet> 

Sortie:

Alaska - Fairbanks, Coldfoot 
North Carolina - Charlotte, Harrisburg 
Virginia - Leesburg, Ashburn 

Remarque: Regroupement par le nom de l'État. Séparateur expression substring fonctionne uniquement avec un style de traction (application des modèles à la ville)

Une solution XSLT 2.0:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:output method="text"/> 
    <xsl:template match="states"> 
     <xsl:for-each-group select="state" group-by="name"> 
      <xsl:sort select="name"/> 
      <xsl:value-of select="concat(name, 
             ' - ', 
             string-join(current-group()/city,', '), 
             '&#xA;')"/> 
     </xsl:for-each-group> 
    </xsl:template> 
</xsl:stylesheet> 

Juste pour le plaisir, cette expression XPath 2.0:

string-join(for $state in distinct-values(/*/*/name) 
      return concat($state, 
          ' - ', 
          string-join(/*/*[name=$state]/city, 
             ', ')), 
      '&#xA;') 
+1

+1, votre homme Alejandro. La solution 1.0 ressemble à la méthode Muenchian, n'est-ce pas? –

+0

@Abe Miessler: Vous avez raison. C'est la méthode Muenchian. C'est le regroupement standar pour XSLT 1.0 –

+0

+1 pour une réponse correcte et courte. –

0

Cela renverra une liste distincte d'états:

<xsl:for-each select="states/state"> 
    <xsl:sort select="name" /> 
    <xsl:if test="not(name = preceding-sibling::state/name)" > 
     <xsl:value-of select="name" /> 
    </xsl:if> 
    </xsl:for-each> 

J'ai utilisé votre exemple XML, construit un peu s feuille de tyle avec ce qui précède, il a couru à travers Xalan-j, et il retourne:

Alaska Caroline du Nord Virginie

donc de vous devriez y être en mesure d'appliquer un modèle ou d'une autre pour-chaque boucle pour tirer la liste des villes pour chaque état distinct.

Chris

+0

C'est la voie à suivre quand vous n'avez pas accès à 'key' (il y a très longtemps). Mais si vous prenez la comparaison comme unité de complexité, vous aurez 1 + (N * N)/2 complexité. Ensuite, vous devez traiter le groupe. Ce sera N '* N complexité avec la comparaison des ensembles de nœuds. De plus, ce test n'est pas nécessaire. Vous pourriez avoir ce test en tant que prédicat ajouté à chaque expression sélectionnée. –