Zählen, Summieren und Runden

(Auszug aus "XSLT 2.0 & XPath 2.0" von Frank Bongers, Kapitel 2.)

Mit XPath-Funktionen ist es auch möglich, Knoten zu zählen und numerische Inhalte zu summieren. Hierzu dienen die Funktionen fn:count() respektive fn:sum(). Eine so gebildete Summe kann XPath auch runden, wobei neben den landläufigen, weitestgehend selbsterklärenden Funktionen fn:round(), fn:floor() und fn:ceiling() auch die originellere Funktion fn:round-half-to-even() zur Verfügung steht. Anhand der Buchliste lassen sich alle demonstrieren.

Zählen mit XPath – fn:count()

Im Dokument befinden sich Bücher mit und ohne Angabe der ISBN. Interes­sant zu wissen wäre, wie viele von jeder Gruppe enthalten sind. Hier hilft die Funktion fn:count(), die sich ihre eigene Sequenz zusammenstellt und die Items zählt. Zunächst werden pauschal alle Bücher gezählt, was am sinnvollsten in der Template-Regel für <buchhandel> geschieht:

<xsl:template match="buchhandel">
  <p><b>Statistik:</b></p>
  <p>Bücher in der Liste: <xsl:value-of select="fn:count(buch)"/></p>
  <xsl:apply-templates select="buch"/>
</xsl:template>

Ausgehend von <buchhandel> als Current Node bildet fn:count(buch) eine Sequenz aller Buch-Elemente und gibt deren Anzahl zurück. Das lässt sich durch Predicates noch präzisieren:

<xsl:template match="buchhandel">
  <p><b>Statistik:</b></p>
  <p>Bücher in der Liste: <xsl:value-of select="fn:count(buch)"/>; davon mit ISBN: <xsl:value-of select="fn:count(buch[@isbn!=''])"/>; ohne ISBN: <xsl:value-of select="fn:count(buch[fn:not(@isbn!='')])"/>;</p>
  <xsl:apply-templates select="buch"/>
</xsl:template>

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum.xsl (Auszug).

Die Ausgabe sieht so aus:

Bücher in der Liste: 27; davon mit ISBN: 12; ohne ISBN: 15;

Summieren mit XPath – fn:sum()

Wollen Sie in Zusammenhang mit dieser Liste den Gesamtpreis aller Bücher erfahren – es sei dahingestellt, wie sinnvoll das ist –, so ist dies mit Hilfe der Funktion fn:sum() darzustellen. Die oben begonnene Statistik wird erweitert:

...
<p>Gesamtpreis aller Bücher: <xsl:value-of select="fn:sum(buch/preis)"/></p>
...

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum.xsl (Auszug).

Der ausgegebene Preis beträgt 612.7, was für Preisangaben nicht den Gepflo­genheiten entspricht. Das ist zu beheben. Vorher wäre noch der Durchschnitt­spreis zu errechnen. Die Zahl der Bücher wurde eben schon mit fn:count() ermit­telt, hier geht dies ebenfalls vonstatten:

<p>Durchschnittspreis aller Bücher: <xsl:value-of select="fn:sum(buch/preis) div fn:count(buch)"/></p>

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum.xsl (Auszug).

Zu beachten ist der Operator div. Das in anderen Sprachen für Division ver­wendete Symbol / ist in XPath anderweitig belegt und kann deshalb nicht in dieser Bedeutung eingesetzt werden. Die anderen Rechenoperatoren sind die gewohnten, also +, , *. Dazu kommen noch mod für Modulo und idiv für Ganzzahldivision. Man käme in Versuchung, Letzteres hier alternativ anzuwen­den (Anmerkung: Mit idiv erhalten Sie einen Fehler, weil dann der erste Operand strikt eine Ganzzahl (Integer) sein muss, was aber hier nicht der Fall ist.); als Ergebnis erhält man nämlich 22.6925925925925925926.

Glücklicherweise bietet XPath auch die Möglichkeit zu runden.

Runden mit XPath – fn:round(), fn:floor(), fn:ceiling(), fn:round-half-to-even()

Zunächst können Sie versuchen, der Situation mit fn:round() Herr zu werden. Dies ist nicht weiter schwer. Der Ausdruck wird allerdings langsam »schachte­lig«:

<p>Durchschnittspreis aller Bücher: <xsl:value-of select="fn:round(fn:sum(buch/preis) div fn:count(buch))"/></p>

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum-round.xsl (Auszug).

Dies rundet nach 23 auf und verhält sich damit identisch zur Funktion fn:cei­ling(), während die Funktion fn:floor() nach 22 abrundet. Richtig befriedi­gend ist dies nicht, denn für Preisangaben wären zwei Nachkomma­stellen genauer.

Genau dies lässt sich mit fn:round-half-to-even() erzielen. Die Funktion nimmt ein zweites Argument entgegen, das die Rundungspräzision beschreibt. Hier eine 2 zu übergeben, bewirkt genau die angestrebte Stellenzahl:

fn:round-half-to-even(fn:sum(buch/preis) div fn:count(buch),2)

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum-roundeven.xsl (Auszug).

Die Ausgabe lautet hiermit:

Durchschnittspreis aller Bücher: 22.69

Zahlen formatieren mit format-number()

Als kleine kosmetische Nachbesserung wäre noch die Erweiterung des Gesamt­preises auf zwei Nachkommastellen zu erledigen. Hier nochmals fn:round-half-to-even() ins Feld zu führen, nützt jedoch nichts; denn diese Funktion kann nur überschüssige Nachkommastellen entfernen, zum Anfügen folgender Nullen ist sie nicht in der Lage.

Dies ist mittels der XSLT-Funktion format-number() zu erreichen. Neben der Eingabezahl nimmt die Funktion einen Muster-String (Anmerkung: Die Funktion ist viel komplexer als hier dargestellt. Hierzu möchte ich auf die Referenz der XSLT- und XPath-Funktionen verweisen.) entgegen, in dem die Stellen durch Platzhal­ter vertreten sind. Dabei markiert # eine Stelle, die entfallen darf, die 0 dagegen eine stets auszugebende Stelle, die im Bedarfsfall eine 0 erhält.

Folgender Aufruf führt hier zum Ziel:

format-number(fn:sum(buch/preis),'##00.00')

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum-format.xsl (Auszug).

Die Ausgabe lautet jetzt:

Gesamtpreis aller Bücher: 612.70

Wollen Sie sichergehen, dass die oben mit fn:round-half-to-even() auf zwei Nachkommastellen getrimmte Zahl auch zuverlässig so ausgegeben wird (es könnte sich ja auch mal ein glatterer Durchschnittspreis ergeben), dann sollten Sie format-number() auch hier zusätzlich anwenden:

format-number(fn:round-half-to-even(fn:sum(buch/preis) div fn:count(buch), 2),'##00.00')

Code-Beispiel: kap02/2.03.3/buchhandel-count-sum-format.xsl (Auszug).

Optisch ändert dies hier nichts, es handelt sich lediglich um einen kleinen Ret­tungsanker. Die Statistik des Buchverzeichnisses soll vorläufig abgeschlos­sen sein. Nun können wir die Reihenfolge der Ausgabe der Bücher angehen.

   

<< zurück vor >>
Tipp der data2type-Redaktion:
Zum Thema XSLT bieten wir auch folgende Schulungen zur Vertiefung und professionellen Fortbildung an:

Copyright © Galileo Press, Bonn 2008
Für Ihren privaten Gebrauch dürfen Sie die Online-Version ausdrucken.
Ansonsten unterliegt dieses Kapitel aus dem Buch "XSLT 2.0 & XPath 2.0 ― Das umfassende Handbuch" denselben Bestimmungen wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Galileo Press, Rheinwerkallee 4, 53227 Bonn