Home
Über mich
Blog
Veröffentlichungen
IT-Trainings
Impressum


XPath 3.1: Array

Zusammenfassung:

Ein Array ist ein neuer Datentyp in XDM 3.1. Spezialfunktionen wie array:size, array:get, array:append, array:subarray, array:remove, array:insert-before, array:head, array:tail, array:reverse, array:for-each, array:for-each-pair, array:join, array:filter, array:sort sowie der Einsatz anonymer Funktionen bieten reichhaltige Unterstützung für flexible Implementierungen.

XPath 3.1: Array

Ein Array der Größe n ist ein Feld oder eine Tabelle mit n Spalten, die in Schleifen von 1 bis n angesprochen werden können. Ein einfaches Beispiel soll das demonstrieren:

<xsl:variable name="varray" select="['Hugo', 'Lotte', 'Theo']"/>

XPath: array:size

Die XPath-Funktion array:size ermittelt die Größe des Arrays:

 <xsl:template match="/" name="xsl:initial-template">
  <root>   
   <xsl:value-of select="array:size($varray)"/>
  </root>
 </xsl:template>

Das Ergebnis dürfte wenig überraschen:

<root>3</root>

Die Größe eines leeren Arrays ...

<xsl:value-of select="array:size([])"/>

ergibt 0:

<root>0</root>

Der folgende Aufruf definiert zwei Arrays, von denen das erste Feld wiederum ein Array darstellt.

<xsl:value-of 
     select="array:size([['Hugo', 'Lotte'], ['Theo']])"/>

Die Größe des Gesamtarrays ist 2:

<root>2</root>

XPath: array:get

Die XPath-Funktion array:get liefert jenen Wert, der an einer bestimmten Indexstelle steht (gezählt wird von 1 an).

 <xsl:template match="/" name="xsl:initial-template">
  <root>   
   <xsl:value-of select="array:get($varray, 2)"/>
  </root>
 </xsl:template>

array:get($varray, 2) gibt daher das Ergebnis "Lotte".

<root>Lotte</root>

Besteht hingegen - wie vorher - ein Feld in einem Array selber aus einem Array,

 <xsl:template match="/" name="xsl:initial-template">
  <root>   
   <xsl:value-of 
        select="array:get([['Hugo', 'Lotte'], ['Theo']], 1)"/>
  </root>
 </xsl:template>

so ist in diesem Fall das Ergebnis:

<root>Hugo Lotte</root>

Etwas interessanter wird es noch, die Einzelwerte des Arrays auszugeben:

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <loop>
    <xsl:for-each select="1 to array:size($varray)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($varray, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

Und auch hierfür gibt es ein Ergebnis:

<root>
 <loop>
  <wert nr="1">Hugo</wert>
  <wert nr="2">Lotte</wert>
  <wert nr="3">Theo</wert>
 </loop>
</root>

XPath: array:append

Die XPath-Funktion array:append fügt einem bestehenden Array ein weiteres Feld hinzu. Dieses neu geschaffene Array können wir umgehend u.a. mit array:size und array:get auswerten:

<xsl:variable name="varray2" 
     select="array:append($varray, 'Resi')"/>
 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <size>
    <xsl:value-of select="array:size($varray2)"/>
   </size>
   <get>
    <xsl:value-of select="array:get($varray2, 4)"/>
   </get>
  </root>
 </xsl:template>

... und erhalten mit der neuen Position 4 dieses Ergebnis:

<root>
 <size>4</size>
 <get>Resi</get>
</root>

XPath: array:subarray

Die XPath-Funktion array:subarray errechnet ein Teil-Array, das mit der gewünschten Start-Position beginnt und alle restlichen Felder übernimmt. Als optionaler zweiter Parameter kann die Anzahl der Felder angegeben werden, die ab der Startposition einbezogen werden sollen.

Beispiel: aus dem ursprünglich definierten "varray" mit den Inhalten ['Hugo', 'Lotte', 'Theo'] basteln wir us ein Subarray "vsubarray", das an der zweiten Stelle (also bei 'Lotte') beginnt und bis zum Ende ('Theo') durchzieht.

 <xsl:variable name="vsubarray" select="array:subarray($varray, 2)"/>

Der optionale zweite Parameter, der die Anzahl der gesuchten Felder beschränkt, würde ein Subarray ['Lotte', 'Theo'] auch dann nur auf die beiden begenzen, wenn das Hauptarray "varray" noch weitere Einträge hätte.

<xsl:variable name="vsubarray" select="array:subarray($varray, 2, 2)"/>

Im vorliegenden Fall macht das keinen Unterschied, und der Aufruf

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <get nr="1">
    <xsl:value-of select="array:get($vsubarray, 1)"/>
   </get>
   <get nr="{array:size($vsubarray)}">
    <xsl:value-of select="array:get($vsubarray, array:size($vsubarray))"/>
   </get>
  </root>
 </xsl:template>

... bringt dieses Ergebnis:

<root>
 <get nr="1">Lotte</get>
 <get nr="2">Theo</get>
</root>

Auch hier ist es sinnvoll, sich die Einzelwerte anzusehen:

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <loop>
    <xsl:for-each select="1 to array:size($vsubarray)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vsubarray, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

Haben Sie etwas anderes erwartet?

<root>
 <loop>
  <wert nr="1">Lotte</wert>
  <wert nr="2">Theo</wert>
 </loop>
</root>

XPath: array:remove

Die XPath-Funktion array:remove ist in der Lage, aus einem Array jenes Feld zu entfernen, das über den Positions-Parameter bestimmt wird.

Beispiel: aus dem ursprünglich definierten "varray" mit den Inhalten ['Hugo', 'Lotte', 'Theo'] basteln wir us ein zweites Array "vremoved", das den Index an der zweiten Stelle ('Lotte') löscht.

<xsl:variable name="vremoved" select="array:remove($varray, 2)"/>

Bei der Schleife über des Rest-Array ...

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <loop>
    <xsl:for-each select="1 to array:size($vremoved)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vremoved, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

bleiben nur noch Hugo und Theo übrig:

<root>
 <loop>
  <wert nr="1">Hugo</wert>
  <wert nr="2">Theo</wert>
 </loop>
</root>

Durch eine Sequenz von Löschpositionen können wir auch mehrere Einzelwerte ansprechen, etwa so:

<xsl:variable name="vremoved" 
     select="array:remove($varray, 2 to 3)"/>

... übrig bliebe dann Hugo:

<root>
 <loop>
  <wert nr="1">Hugo</wert>
 </loop>
</root>

... oder durch gezielte Einzelwerte:

<xsl:variable name="vremoved" 
     select="array:remove($varray, (1,3))"/>

wenn die Felder an der Stelle 1 und 3 gelöscht werden, dann bleibt nur Lotte übrig:

<root>
 <loop>
  <wert nr="1">Lotte</wert>
 </loop>
</root>

XPath: array:insert-before

Die XPath-Funktion array:insert-before erlaubt, an einer bestimmten Position ein zusätzlichen Wert einzufügen.

Beispiel: in das ursprünglich definierte "varray" mit den Inhalten ['Hugo', 'Lotte', 'Theo'] fügen wir an 3. Stelle den Wert "Tanja" ein.

<xsl:variable name="vinserted" 
     select="array:insert-before($varray, 3, 'Tanja')"/>

Der Aufruf mit array:size und array:get ist wie gehabt:

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <loop>
    <xsl:for-each select="1 to array:size($vinserted)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vinserted, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

... und bringt ein nachvollziehbares Ergebnis:

<root>
 <loop>
  <wert nr="1">Hugo</wert>
  <wert nr="2">Lotte</wert>
  <wert nr="3">Tanja</wert>
  <wert nr="4">Theo</wert>
 </loop>
</root>

XPath: array:head

Die XPath-Funktion array:head gibt das erste Item des Arrays zurück:

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <head>
    <xsl:value-of select="array:head($varray)"/>
   </head>
  </root>
 </xsl:template>
<root>
 <head>Hugo</head>
</root>

XPath: array:tail

Die XPath-Funktion array:tail gibt ab dem zweiten Item (also ohne das erste Item, das über array:head erreicht werden kann) alle Folge-Items zurück.

<xsl:variable name="vtail" select="array:tail($varray)"/>

Der Loop über diese Folge-Items ...

 <xsl:template match="/" name="xsl:initial-template">
  <root>
   <loop>
    <xsl:for-each select="1 to array:size($vtail)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vtail, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

bringt daher dieses Ergebnis:

<root>
 <loop>
  <wert nr="1">Lotte</wert>
  <wert nr="2">Theo</wert>
 </loop>
</root>

XPath: array:reverse

Die XPath-Funktion array:reverse gibt ein Array zurück, dessen Items in umgekehrter Reihenfolge angeordnet sind.

Beispiel: das ursprünglich definierte "varray" mit den Inhalten ['Hugo', 'Lotte', 'Theo'] wird in der Reihenfolge ['Theo', 'Lotte', 'Hugo'] ausgewertet:

 <xsl:variable name="vreverse" select="array:reverse($varray)"/>
 <xsl:template match="/" name="xsl:initial-template">
  <root>   
   <loop>
    <xsl:for-each select="1 to array:size($vreverse)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vreverse, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

Ich denke, dazu muß ich nicht mehr viel sagen:

<root>
 <loop>
  <wert nr="1">Theo</wert>
  <wert nr="2">Lotte</wert>
  <wert nr="3">Hugo</wert>
 </loop>
</root>

XPath: array:join

Die XPath-Funktion array:join verkettet den Inhalt diverser Arrays in ein einzelnes Array.

Die Variable "vjoin" macht genau dieses: sie verkettet drei Arrays in ein einzelnes. Die Einzelarrays bestehen aus:

<xsl:variable name="vjoin" 
     select="array:join((['Hugo', 'Lotte'], ['Theo', 'Tanja'], ['Maxi']))"/>

Der abermalige Aufruf mit Hilfe von array:size und array:get ...

 <xsl:template match="/" name="xsl:initial-template"> 
  <root>   
   <loop>
    <xsl:for-each select="1 to array:size($vjoin)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vjoin, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

... bringt dieses Resultat ans Licht:

<root>
 <loop>
  <wert nr="1">Hugo</wert>
  <wert nr="2">Lotte</wert>
  <wert nr="3">Theo</wert>
  <wert nr="4">Tanja</wert>
  <wert nr="5">Maxi</wert>
 </loop>
</root>

XPath: array:for-each

Die XPath-Funktion array:for-each ermöglicht den flexiblen Einsatz anonymer Funktionen, um Arrays zu modifizieren.

Die Variable "vforeach" nimmt das ursprünglich definierte "varray" mit den Inhalten ['Hugo', 'Lotte', 'Theo'] unter die Lupe. Wenn der jeweilige Einzelwert $v einem Pattern 'Lotte' entspricht, dann soll der Inhalt mit fn:upper-case in Großbuchstaben verwandelt werden, sonst mit Hilfe von lower-case in Kleinbuchstaben.

<xsl:variable name="vforeach" 
     select="array:for-each($varray, 
             function($v){ if (fn:matches($v, 'Lotte') ) 
                           then ( fn:upper-case($v) ) 
                           else ( fn:lower-case($v) ) })"/>

Der Aufruf von "vforeach" erfolgt wie gehabt:

 <xsl:template match="/" name="xsl:initial-template"> 
  <root>   
   <loop>
    <xsl:for-each select="1 to array:size($vforeach)">
     <xsl:variable name="vpos" select="fn:position()"/>
     <wert nr="{$vpos}">
      <xsl:value-of select="array:get($vforeach, $vpos)"/>
     </wert>
    </xsl:for-each>
   </loop>
  </root>
 </xsl:template>

... und ruft dieses Resultat hervor:

<root>
 <loop>
  <wert nr="1">hugo</wert>
  <wert nr="2">LOTTE</wert>
  <wert nr="3">theo</wert>
 </loop>
</root>

XPath: Array generieren

Ach ja, wie können wir ein XPath-Array überhaupt automatisch generieren? Nun, recht einfach:

<xsl:variable name="vgenerated" 
     select="array{ (1 to 3), (4 to 6), (7 to 9)}"/>

Der bereits bekannte Aufruf zaubert daraus:

<root>
 <loop>
  <wert nr="1">1</wert>
  <wert nr="2">2</wert>
  <wert nr="3">3</wert>
  <wert nr="4">4</wert>
  <wert nr="5">5</wert>
  <wert nr="6">6</wert>
  <wert nr="7">7</wert>
  <wert nr="8">8</wert>
  <wert nr="9">9</wert>
 </loop>
</root>

In Verbindung mit dem soeben beschriebenen "array:for-each" kann das Ganze dann so aussehen:

<xsl:variable name="vforeach2" 
     select="array:for-each(
               array{ 'Hugo', 'Lotte', (1 to 9), 'Theo' }, 
               function($v){ if ($v instance of xs:integer  ) 
                             then ( $v ) 
                             else () })"/>

Der Aufruf ergibt dann ein Ergebnis, das alle ursprünglichen Felder zwar anspricht, die Werte auf bestimmten Feldern aber leer sind.

<root>
 <loop>
  <wert nr="1"/>
  <wert nr="2"/>
  <wert nr="3">1</wert>
  <wert nr="4">2</wert>
  <wert nr="5">3</wert>
  <wert nr="6">4</wert>
  <wert nr="7">5</wert>
  <wert nr="8">6</wert>
  <wert nr="9">7</wert>
  <wert nr="10">8</wert>
  <wert nr="11">9</wert>
  <wert nr="12"/>
 </loop>
</root>

XPath: array:filter

Die XPath-Funktion array:filter generiert ein Array mit allen Einträgen, für die die Funktion true() zurückgibt.

Das bereits besprochene Array besteht aus einzelnen Zeichenketten sowie aus Zahlen. Die Funktion prüft, ob der jeweilige Wert einem regulären Ausdruck entspricht.

<xsl:variable name="vfilter" 
     select="array:filter(
             array{ 'Hugo', 'Lotte', (1 to 9), 'Theo' }, 
             function($v){ fn:matches(
                    xs:string($v), '[A-Z]{1}[a-z]{1,10}') })"/>

Das ist wieder einmal nur bei Hugo, Lotte und Theo der Fall.

<root>
 <loop>
  <wert nr="1">Hugo</wert>
  <wert nr="2">Lotte</wert>
  <wert nr="3">Theo</wert>
 </loop>
</root>

XPath: array:for-each-pair

Die XPath-Funktion array:for-each-pair gibt ein Array zurück, das seine Einzelwerte aus jedem Member-Paar an derselben Position bezieht. Zur Berechnung des Ergebnisses wird eine Funktion definiert.

Im vorliegenden Fall werden die beiden Werte multipliziert.

<xsl:variable name="vforeachpair" 
     select="array:for-each-pair(
             array{ (1 to 3) }, 
             array{ (7 to 9) }, 
             function($p1, $p2){ $p1 * $p2 })"/>

Im Einzelnen kommen folgende Ergebnisse zusammen:

Das Ergebnis liest sich so:
<root>
 <loop>
  <wert nr="1">7</wert>
  <wert nr="2">16</wert>
  <wert nr="3">27</wert>
 </loop>
</root>

XPath: array:sort

Die XPath-Funktion array:sort generiert ein Array, das alle Items des ursprünglichen Arrays beinhaltet, jedoch in sortierter Reihenfolge. Dabei ist es möglich, optional eine Collation sowie - ebenfalls optional - eine Function einzubinden.

Das einfachste Beispiel finden wir hier: die generierte Reihenfolge 4,5,6,1,2,3,7,8,9 ...

<xsl:variable name="vsorted" 
     select="array:sort( array{ (4 to 6), (1 to 3),  (7 to 9)})"/> 

wird durch array:sort aufsteigend sortiert:

<root>
 <loop>
  <wert nr="1">1</wert>
  <wert nr="2">2</wert>
  <wert nr="3">3</wert>
  <wert nr="4">4</wert>
  <wert nr="5">5</wert>
  <wert nr="6">6</wert>
  <wert nr="7">7</wert>
  <wert nr="8">8</wert>
  <wert nr="9">9</wert>
 </loop>
</root>

Der Einsatz einer Collation sorgt dafür, daß beispielsweise bestimmte Sonderzeichen wie "ß" gleichwertig mit "ss" behandelt werden.

<xsl:variable name="vsorted2" 
     select="array:sort(
             ['Straße2', 'Strasse1', 'Weg', 'Straße1', 'Strasse2', 'Pfad'], 
             'http://www.w3.org/2013/collation/UCA?lang=de;strength=primary')"/>

Sortierungsergebnis ohne Verwendung der Collation:

<root>
 <loop>
  <wert nr="1">Pfad</wert>
  <wert nr="2">Strasse1</wert>
  <wert nr="3">Strasse2</wert>
  <wert nr="4">Straße1</wert>
  <wert nr="5">Straße2</wert>
  <wert nr="6">Weg</wert>
 </loop>
</root>

Sortierungsergebnis mit Verwendung der Collation:

<root>
 <loop>
  <wert nr="1">Pfad</wert>
  <wert nr="2">Strasse1</wert>
  <wert nr="3">Straße1</wert>
  <wert nr="4">Straße2</wert>
  <wert nr="5">Strasse2</wert>
  <wert nr="6">Weg</wert>
 </loop>
</root>

qrpic/xpath_array.jpg

wg / 14. Oktober 2017




Fragen? Anmerkungen? Tips?

Bitte nehmen Sie Kontakt zu mir auf (info10@wilfried-grupe.de).



Vielen Dank für Ihr Interesse an meiner Arbeit.


V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn

Mobil: 0151. 750 360 61 * eMail: info10@wilfried-grupe.de