Holge r: Sortierproblem bei Index

Hallo,

ich wuerde gerne knoten sortieren, die ein Art Indexstruktur besitzen

Beispiel:

<knoten>1.1.1.1</knoten>
<knoten>1.1.1.15</knoten>
<knoten>1.1.1.1000</knoten>
<knoten>1.1.1.10</knoten>
<knoten>1.1.1.12</knoten>

wie sortiere ich nun das mit es in indexfolge ausgegeben wird?
1.1.1.1
1.1.1.10
1.1.1.12
1.1.1.15
1.1.1.1000

Ich koennte auch xslt2.0 einsetzen und eine funktion verwenden.

gruß, H.

  1. Hallo Holge r,

    Ich koennte auch xslt2.0 einsetzen und eine funktion verwenden.

    Wenn XSLT/XPath 2.0 zur Verfügung steht, bietet sich eine Sortierung über die fn:tokenize()-Funktion an:

    <xsl:apply-templates select="knoten">  
      <xsl:sort select="fn:number(fn:tokenize(.,'\.')[1])" data-type="number" order="ascending"/>  
      <xsl:sort select="fn:number(fn:tokenize(.,'\.')[2])" data-type="number" order="ascending"/>  
      <xsl:sort select="fn:number(fn:tokenize(.,'\.')[3])" data-type="number" order="ascending"/>  
      <xsl:sort select="fn:number(fn:tokenize(.,'\.')[4])" data-type="number" order="ascending"/>  
    </xsl:apply-templates>
    

    Grüße,
    Thomas

    1. Danke Thomas,

      das hatte ich so ähnlich in Betracht bezogen. Problem ist nur das der Index nicht auf 4 Level begrenzt ist und manchmal ziemlich "tief" werden kann. Man könnte ja irgendwie die anzahl der '.' zählen, aber ich dachte es gibt da noch etwas Elegantes. Kann mir nicht ganz vorstellen, dass ich da der einzige bin, eine lösung wäre rekursive template mit dem jeweilige prefix merken, aber elegant wäre es halt mit xsl:sort

      Gruß, H.

      1. Hallo Holge,

        Kann mir nicht ganz vorstellen, dass ich da der einzige bin, eine lösung wäre rekursive template mit dem jeweilige prefix merken, aber elegant wäre es halt mit xsl:sort

        Optimal wäre sicher, die Vergleichsfunktion auszutauschen. XSLT 2 bzw. XPath 2 sieht eine Möglichkeit vor, verschiedene Vergleichsmodi für Strings auszuwählen. Allerdings ist der Mechanismus wohl primär für sprachabhängige Vergleiche gedacht und es gibt keinen Weg, da in XSLT selbst einen Vergleich zu definieren. Das könnte höchstens über Mechanismen des XSLT-Prozessors selbst gehen.

        Ich sehe zwei Lösungungsansätze:
        1. Man versucht, die Versionsnummern irgendwie auf was normal sortierbares durch eine eigene Funktion abzubilden. Also aus "1.1.1.1" -> "001.001.001.001". Das geht ja mit <sort select="funktion(...)"/>
        2. Man schreibt eine rekursive Funktion, die nach einer bestimmten Stufe sortiert. Man kann angeben, dass die Sortierung stabil sein soll. Also könnte man die Teilnummern in umgekehrter Reiehnfolge sortieren.

        Grüße

        Daniel

        1. Danke für die Infos,

          ich denke ich werde erst einmal ein rekursives Template benutzen, da das stylesheet nicht gar zu komplex ist. Werde mich aber mal bei etwas mehr zeit mit dieser Problematik auseinadersetzen, das Problem dürfte weiss gott nicht unüblich sein.

      2. Hallo Holge r,

        Problem ist nur das der Index nicht auf 4 Level begrenzt ist und manchmal ziemlich "tief" werden kann. Man könnte ja irgendwie die anzahl der '.' zählen, aber ich dachte es gibt da noch etwas Elegantes.

        Hier mal ein allgemeinerer Versuch mit Zählung der durch Punkte getrennten Teile und Sortierung über die Zusammensetzung mit führenden Nullen.

        Aus 1.1.1.1 wird 0001000100010001
        und aus 1.1.1.1000 wird 0001000100011000
        usw.

        Probiere es mal damit:

        <?xml version="1.0" encoding="ISO-8859-1"?>  
        <xsl:stylesheet version="2.0"  
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"  
          xmlns:fn="http://www.w3.org/2005/xpath-functions"  
          xmlns:xs="http://www.w3.org/2001/XMLSchema"  
          exclude-result-prefixes="fn xs">  
          
        <xsl:template match="wurzel">  
          
        <xsl:variable name="n" select="fn:max(for $k in knoten return fn:count(fn:tokenize($k,'\.')))"/>  
          
        <xsl:apply-templates select="knoten">  
          <xsl:sort select="fn:number(fn:string-join(for $i in (1 to $n) return fn:concat(fn:string-join(for $i in (1 to 4 - fn:string-length(fn:tokenize(.,'\.')[$i])) return '0',''), fn:tokenize(.,'\.')[$i]),''))" data-type="number" order="ascending"/>  
        </xsl:apply-templates>  
          
        </xsl:template>  
          
        <xsl:template match="knoten">  
          <xsl:value-of select="."/><br />  
        </xsl:template>  
          
        </xsl:stylesheet>
        

        Getestet am XML-Beispiel:

        <?xml version="1.0" encoding="ISO-8859-1"?>  
        <wurzel>  
          <knoten>1.1.1.1</knoten>  
          <knoten>1.1.1.15</knoten>  
          <knoten>4.1.1.120</knoten>  
          <knoten>1.1.1.1000</knoten>  
          <knoten>1.10.1.10</knoten>  
          <knoten>2.200.25.10</knoten>  
          <knoten>1.1.1.12</knoten>  
        </wurzel>
        

        Ergebnis:
        1.1.1.1
        1.1.1.12
        1.1.1.15
        1.1.1.1000
        1.10.1.10
        2.200.25.10
        4.1.1.120

        Die XSL-Codestelle ... 1 to 4 ... bezieht sich auf die Anzahl der Stellen pro Segment, hier also bis 4 Stellen. Diese Zahl könnte auch noch dynamisch ermittelt werden ...

        Grüße,
        Thomas

        1. Nachtrag zu:

          Die XSL-Codestelle ... 1 to 4 ... bezieht sich auf die Anzahl der Stellen pro Segment, hier also bis 4 Stellen. Diese Zahl könnte auch noch dynamisch ermittelt werden ...

          ... hier in Form der Variable $s:

          <xsl:variable name="n" select="fn:max(for $k in knoten return fn:count(fn:tokenize($k,'\.')))"/>  
          <xsl:variable name="s" select="fn:max(for $j in 1 to $n return for $k in knoten return fn:string-length(fn:tokenize($k,'\.')[$j]))"/>  
            
          <xsl:apply-templates select="knoten">  
            <xsl:sort select="fn:number(fn:string-join(for $i in (1 to $n) return fn:concat(fn:string-join(for $i in (1 to $s - fn:string-length(fn:tokenize(.,'\.')[$i])) return '0',''), fn:tokenize(.,'\.')[$i]),''))" data-type="number" order="ascending"/>  
          </xsl:apply-templates>
          

          Grüße,
          Thomas