XSL / Die XSLT - Struktur / xsl:sort, xsl:perform-sort, fn:sort
![]() |
![]() |
➪ Eine häufige Aufgabe ist das Sortieren von Ergebnissen. Hier finden Sie in xsl:sort, xsl:perform-sort und fn:sort leistungsfähige Unterstützung.
Auf dieser Seite:Siehe
Schauen Sie sich zunächst die Logik ohne Sortierung an.
<xsl:for-each select="//Gehalt">
<einkommen>
<xsl:value-of select="."/>
</einkommen>
</xsl:for-each>
Das (noch unsortierte) Ergebnis sieht so aus:
<einkommen>234.56</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>5430</einkommen>
<einkommen>321.45</einkommen>
<einkommen>987.58</einkommen>
<einkommen>654.21</einkommen>
<einkommen>333.33</einkommen>
<einkommen>456</einkommen>
<einkommen>876.54</einkommen>
<einkommen>546.77</einkommen>
<einkommen>777.77</einkommen>
<einkommen>357</einkommen>
<einkommen>6789</einkommen>
<einkommen>234</einkommen>
<einkommen>321</einkommen>
<einkommen>456</einkommen>
<einkommen>3450</einkommen>
<einkommen>222</einkommen>
<einkommen>135</einkommen>
Nun erfolgt die Sortierung mit xsl:sort. Da die Default-Sortierung data-type="text" ist, muss der Prozessor angewiesen werden, die Einzelwerte als number zu erkennen. order="descending definiert die absteigende Sortierung, Alternative: order="ascending".
<xsl:for-each select="//Gehalt">
<xsl:sort
select="."
data-type="number"
order="descending"/>
<einkommen>
<xsl:value-of select="."/>
</einkommen>
</xsl:for-each>
Ich denke, das Ergebnis ist nachvollziehbar:
<einkommen>6789</einkommen>
<einkommen>5430</einkommen>
<einkommen>3450</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>987.58</einkommen>
<einkommen>876.54</einkommen>
<einkommen>777.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>546.77</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>357</einkommen>
<einkommen>333.33</einkommen>
<einkommen>321.45</einkommen>
<einkommen>321</einkommen>
<einkommen>234.56</einkommen>
<einkommen>234</einkommen>
<einkommen>222</einkommen>
<einkommen>135</einkommen>
Nun gibt es auch die Möglichkeit, die Sortierung direkt in XPath aufzurufen:
<xsl:for-each select="sort(//Gehalt)">
<einkommen>
<xsl:value-of select="."/>
</einkommen>
</xsl:for-each>
Per Default wird dabei jedes Item als Text angesehen, daher fällt das Sortierergebnis vorläufig so aus:
<einkommen>1234.56</einkommen>
<einkommen>135</einkommen>
<einkommen>222</einkommen>
<einkommen>234</einkommen>
<einkommen>234.56</einkommen>
<einkommen>321</einkommen>
<einkommen>321.45</einkommen>
<einkommen>333.33</einkommen>
<einkommen>3450</einkommen>
<einkommen>357</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>5430</einkommen>
<einkommen>546.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>6789</einkommen>
<einkommen>777.77</einkommen>
<einkommen>876.54</einkommen>
<einkommen>987.58</einkommen>
Durch ein einfaches Type-Casting auf xs:decimal wird jedes Item als Zahl anerkannt ...
<xsl:for-each select="sort(//xs:decimal(Gehalt))">
<einkommen>
<xsl:value-of select="."/>
</einkommen>
</xsl:for-each>
... und entsprechend sortiert:
<einkommen>135</einkommen>
<einkommen>222</einkommen>
<einkommen>234</einkommen>
<einkommen>234.56</einkommen>
<einkommen>321</einkommen>
<einkommen>321.45</einkommen>
<einkommen>333.33</einkommen>
<einkommen>357</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>546.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>777.77</einkommen>
<einkommen>876.54</einkommen>
<einkommen>987.58</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>3450</einkommen>
<einkommen>5430</einkommen>
<einkommen>6789</einkommen>
Die absteigende Sortierung ist Ihnen lieber? Bitte sehr, mit reverse funktioniert auch das:
<xsl:for-each select="reverse(sort(//xs:decimal(Gehalt)))">
<einkommen>
<xsl:value-of select="."/>
</einkommen>
</xsl:for-each>
Die Alternative über XPath 3.0 und dem => Operator ergibt zunächst die Liste der Items:
//Mensch/xs:decimal(Gehalt)
=> sort()
=> reverse()
... und - in XQuery - mit dem gewünschten Element einkommen:
//Mensch/xs:decimal(Gehalt)
=> sort()
=> reverse()
=> for-each(
function($w){
element {'einkommen'}{$w}
}
)
Ergänzend gibt es die Möglichkeit, durch Einsatz von anonymen Funktionen den Sortierungsschlüssel zu verändern.
Die im Folgenden implementierte Funktion multipliziert den jeweiligen Dezimalwert mit 10, wenn er ursprünglich unter 300 lag.
<xsl:for-each
select="sort(//xs:decimal(Gehalt), '',
function($v){ if ($v < 300 )
then ( $v * 10 )
else ( $v ) })">
<einkommen>
<xsl:value-of select="."/>
</einkommen>
</xsl:for-each>
Dadurch erhalten die Einzelwerte einen effektiven internen Sortierwert in Höhe von:
135 -> 1350
222 -> 2220
234 -> 2340
234.56 -> 2345.6
... und das führt letztlich zu einem modifizierten Ergebnis:
<einkommen>321</einkommen>
<einkommen>321.45</einkommen>
<einkommen>333.33</einkommen>
<einkommen>357</einkommen>
<einkommen>456</einkommen>
<einkommen>456</einkommen>
<einkommen>546.77</einkommen>
<einkommen>654.21</einkommen>
<einkommen>777.77</einkommen>
<einkommen>876.54</einkommen>
<einkommen>987.58</einkommen>
<einkommen>1234.56</einkommen>
<einkommen>135</einkommen>
<einkommen>222</einkommen>
<einkommen>234</einkommen>
<einkommen>234.56</einkommen>
<einkommen>3450</einkommen>
<einkommen>5430</einkommen>
<einkommen>6789</einkommen>
Bei der textbasierten Sortierung können Collations eine wichtige Rolle spielen. Freilich akzeptiert nicht jeder Prozessor die Collation http://www.w3.org/2013/collation/UCA?lang=de;strength=primary. Hier bietet sich das lang-Attribut von xsl:sort an.
<xsl:template name="sortByCollation">
<xsl:variable name="vfeld">
<i>Straße2</i>
<i>Strasse1</i>
<i>Weg</i>
<i>Straße1</i>
<i>Strasse2</i>
<i>Pfad</i>
</xsl:variable>
<xsl:for-each select="$vfeld/i">
<xsl:sort select="." lang="de"/>
<!-- anstelle von
<xsl:sort
select="."
collation="...s.o. "/>
-->
<y>
<xsl:value-of select="."/>
</y>
</xsl:for-each>
</xsl:template>
Und hiermit erfolgt auch diese Sortierung korrekt:
<root>
<y>Pfad</y>
<y>Strasse1</y>
<y>Straße1</y>
<y>Straße2</y>
<y>Strasse2</y>
<y>Weg</y>
</root>
Natürlich ist es auch möglich, mehrere Sortierungen durchzuführen; im Beispiel fülle ich eine Variable v1 mit einer sortierten Sequenz.
<xsl:variable name="v1">
<xsl:for-each
select="//Mensch">
<xsl:sort
select="../name"
data-type="text"
order="descending"/>
<xsl:sort
select="Gehalt"
data-type="number"
order="ascending"/>
<xsl:sequence select="."/>
</xsl:for-each>
</xsl:variable>
Alternativ können Sie ab XSLT Version 2.0 dieselbe Sortierung auch mit xsl:perform-sort durchführen, auch ohne Verwendung von xsl:for-each oder xsl:apply-templates.
<xsl:variable name="v2">
<xsl:perform-sort
select="//Mensch">
<xsl:sort
select="../name"
data-type="text"
order="descending"/>
<xsl:sort
select="Gehalt"
data-type="number"
order="ascending"/>
</xsl:perform-sort>
</xsl:variable>
wg / 10. August 2020
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de