2016-12-09 3 views
0

Angenommen, es gibt dieses XML-Fragment:Wie werden mehrere benachbarte Elemente in XSLT durch "Spalten" gruppiert?

<a>t1</a> <a>t2</a> 
<b>t3</b> <b>t4</b> <b>t5</b> 
<c>t6</c> 

Ich möchte so iterieren, dass die im ersten Durchgang iteriert Elemente sind:

<a>t1</a> <a>t2</a> <b>t3</b> <b>t4</b> <b>t5</b> <c>t6</c> 

Wir als visualisieren konnte

<a>t1</a> <b>t3</b> <c>t6</c> 

im zweiten Durchlauf:

<a>t2</a> <b>t4</b> 

und dritte:

<b>t5</b> 

Die obigen Daten ist nur ein Beispiel. Es ist möglich, längere Sequenzen benachbarter Geschwister und nicht nur diesen festen Datensatz zu haben.

Die Anforderung ist, dass jede Gruppe Elemente enthält, die die gleiche Anzahl von vorhergehenden Geschwistern teilen, die denselben Elementnamen verwenden.

Zum Beispiel in der ersten 'Spalte' < a/>, < b/> und < c/> hat keine vorherigen Geschwister mit dem gleichen Namen bezeichnet.

Die zweite 'Spalte' < a/> und < b/> haben vorhergehende Geschwister mit dem gleichen Namen von 1 zählen, respectively.

Ich möchte in der Lage sein, die Elemente in einer For-Each-Group-Anweisung auf diese Weise zu iterieren, aber ich bin unsicher, wie die Group-By-Klausel auszudrücken ist.

+0

Dies ist unklar. Es gibt keinen semantischen Unterschied zwischen dem ersten und dem zweiten Block. Was meinst du mit "Gruppe"? Sie müssen den _output_ anzeigen, den Sie von der Eingabe erzeugen möchten. Das Einfügen von Zeilenumbrüchen nach jeder Zeichenfolge mit Tags mit demselben Namen ist keine sinnvolle Umwandlung. –

+0

Ich habe die Frage mit hoffentlich einer klareren Beschreibung aktualisiert. – dave

Antwort

2

Die Vorlage

<xsl:template match="div"> 
    <xsl:for-each-group select="*" group-by="count(preceding-sibling::*[node-name(.) = node-name(current())])"> 
     <group key="{current-grouping-key()}"> 
      <xsl:copy-of select="current-group()"/> 
     </group> 
    </xsl:for-each-group> 
</xsl:template> 

verwandelt

<div> 
    <a>t1</a> <a>t2</a> <b>t3</b> <b>t4</b> <b>t5</b> <c>t6</c> 
</div> 

in

<group key="0"> 
    <a>t1</a> 
    <b>t3</b> 
    <c>t6</c> 
</group> 
<group key="1"> 
    <a>t2</a> 
    <b>t4</b> 
</group> 
<group key="2"> 
    <b>t5</b> 
</group> 
0

Die derzeit akzeptierte Antwort hat O (N^2) Zeitkomplexität, weil es preceding-sibling::* für jedes Element verwendet .

Hier ist eine Lösung, die effizient mehr sein kann - keine preceding-sibling::* Achse verwendet:

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

    <xsl:template match="/*"> 
    <xsl:variable name="vTop" select="."/> 
    <xsl:variable name="vNames" select="distinct-values(*/name())"/> 
    <xsl:variable name="vCountNames" select="count($vNames)"/> 

    <xsl:for-each select="1 to $vCountNames"> 
     <xsl:variable name="vCol" select="position()"/> 
     <xsl:for-each select="$vNames"> 
     <xsl:apply-templates select="$vTop/*[name() eq current()][$vCol]"/> 
     </xsl:for-each> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

Wenn diese Transformation auf die folgende XML-Dokument (Das bereitgestellte Fragment von einem oberen (Dokument umgeben angewendet wird Element)):

<t> 
<a>t1</a> <a>t2</a> 
<b>t3</b> <b>t4</b> <b>t5</b> 
<c>t6</c> 
</t> 

Das gewünschte Ergebnis erzeugt wird (die Werte jedes e lement, wenn sie von Spalten durchlaufen):

t1t3t6t2t4t5 

O(N * M) Diese Lösung ist, wo N die Anzahl der Elemente ist und M die Anzahl ihrer unterschiedlichen Namen.

Also, wenn N = k times M dann wird diese Lösung asymptotisch k mal schneller als eine O(N^2) Lösung sein.


II. Ein einzelner, reine XPath 2.0-Ausdruck, die Elemente in der Spalte weises Besuch:

for $vTop in /*, 
     $vCol in 1 to count(distinct-values($vTop/*/name())), 
     $vName in distinct-values($vTop/*/name()) 
    return 
     $vTop/*[name() eq $vName][$vCol] 

XSLT-basierte Verifikation:

<xsl:template match="/*"> 
    <xsl:sequence select= 
    "for $vTop in /*, 
      $vCol in 1 to count(distinct-values($vTop/*/name())), 
      $vName in distinct-values($vTop/*/name()) 
     return 
      $vTop/*[name() eq $vName][$vCol] 
    "/> 
    </xsl:template> 
</xsl:stylesheet> 

Wenn auf dem gleichen XML-Dokument angewandt , Diese Umwandlung wertet den XPath-Ausdruck aus und gibt das Ergebnis dieser Auswertung aus:

t1t3t6t2t4t5 

III. XSLT 1.0 Lösung:

Diese Transformation:

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

<xsl:key name="kByName" match="/*/*" use="name()"/> 

<xsl:variable name="vDistinctNamed" select= 
    "/*/*[generate-id() = generate-id(key('kByName', name())[1])]"/> 

    <xsl:variable name="vNumCols"> 
    <xsl:for-each select="/*/*[generate-id() = generate-id(key('kByName', name())[1])]"> 
     <xsl:sort select= 
     "count(key('kByName', name()))" data-type="number" order="descending"/> 
     <xsl:if test="position()=1"> 
     <xsl:value-of select="count(key('kByName', name()))"/> 
     </xsl:if> 
    </xsl:for-each> 
    </xsl:variable> 

    <xsl:template match="/*"> 
    <xsl:for-each select="*[not(position() > $vNumCols)]"> 
     <xsl:variable name="vCol" select="position()"/> 
     <xsl:for-each select="$vDistinctNamed"> 
     <xsl:variable name="vthisElement" select="/*/*[name() = name(current())][$vCol]"/> 
     <xsl:if test="$vthisElement"> 
      <xsl:value-of select="concat(/*/*[name() = name(current())][$vCol],', ')"/> 
     </xsl:if> 
     </xsl:for-each> 
    </xsl:for-each> 
    </xsl:template> 
</xsl:stylesheet> 

, wenn auf dem gleichen XML-Dokument angewendet wird, erzeugt die gleiche korrekte Ergebnis:

t1, t3, t6, t2, t4, t5, 
Verwandte Themen