XPath / XPath-Funktionen / Zahlenfunktionen / sum()
![]() |
![]() |
➪ Die sum-Funktion berechnet die Summe der Zahlenwerte in der Sequenz (vorausgesetzt, es sind nur Werte enthalten, die auch als Zahlenwerte gecastet werden können). Die sum()-Funktion steht bereits seit XSLT 1.0/XPath 1.0 zur Verfügung, erhält in höheren Versionen (XPath 2.0, XPath 3.0) jedoch komfortable Erweiterungen.
Auf dieser Seite:Signaturen:
sum($arg as xs:anyAtomicType*)
as xs:anyAtomicType
sum($arg as xs:anyAtomicType*,
$zero as xs:anyAtomicType?)
as xs:anyAtomicType?
Siehe https://www.w3.org/TR/xpath-functions-31/func-sum
Siehe https://www.w3.org/TR/xpath-functions-30/func-sum
Nehmen Sie eine Sequenz von Items, die sich aus einem XML-Dokument auswerten ansprechen lässt. Zum Beispiel:
<Orte>
<Ort name="Neustadt">
<Mensch VN="Hugo" NN="Holzflos" Ausgaben="3563.1400000000003">
<Gehalt>234.56</Gehalt>
</Mensch>
<Mensch VN="Nicole" NN="Nixlos" Ausgaben="961.6300000000001">
<Gehalt>1234.56</Gehalt>
</Mensch>
<Mensch VN="Stefan" NN="Sprachlos" Ausgaben="4288.02">
<Gehalt>5430</Gehalt>
</Mensch>
<Mensch VN="Stefan" NN="Sagblos" Ausgaben="2753.9399999999996">
<Gehalt>321.45</Gehalt>
</Mensch>
<Mensch VN="Siggi" NN="Sorglos" Ausgaben="2610.96">
<Gehalt>987.58</Gehalt>
</Mensch>
<Mensch VN="Heini" NN="Herzlos" Ausgaben="415.76">
<Gehalt>654.21</Gehalt>
</Mensch>
</Ort>
<Ort name="Darmstadt">
<Mensch VN="Rudi" NN="Rhodos" Ausgaben="740.6999999999999">
<Gehalt>333.33</Gehalt>
</Mensch>
<Mensch VN="Karl" NN="Kolos" Ausgaben="181.92">
<Gehalt>456</Gehalt>
</Mensch>
<Mensch VN="Simone" NN="Sinnlos" Ausgaben="155.94">
<Gehalt>876.54</Gehalt>
</Mensch>
<Mensch VN="Horst" NN="Hirnlos" Ausgaben="38.97">
<Gehalt>546.77</Gehalt>
</Mensch>
<Mensch VN="Werner" NN="Wertlos" Ausgaben="103.96">
<Gehalt>777.77</Gehalt>
</Mensch>
<Mensch VN="Ludwig" NN="Lustlos" Ausgaben="0">
<Gehalt>357</Gehalt>
</Mensch>
</Ort>
<Ort name="Kapstadt">
<Mensch VN="Willi" NN="Wasistlos" Ausgaben="831.44">
<Gehalt>6789</Gehalt>
</Mensch>
<Mensch VN="Rita" NN="Ruhelos" Ausgaben="203.19">
<Gehalt>234</Gehalt>
</Mensch>
<Mensch VN="Susi" NN="Schlaflos" Ausgaben="808.13">
<Gehalt>321</Gehalt>
</Mensch>
<Mensch VN="Lotte" NN="Rielos" Ausgaben="1555.98">
<Gehalt>456</Gehalt>
</Mensch>
<Mensch VN="Betty" NN="Bodenlos" Ausgaben="324.81999999999994">
<Gehalt>3450</Gehalt>
</Mensch>
<Mensch VN="Martin" NN="Muehelos" Ausgaben="142.89000000000001">
<Gehalt>222</Gehalt>
</Mensch>
<Mensch VN="Liane" NN="Leinenlos" Ausgaben="964.0699999999999">
<Gehalt>135</Gehalt>
</Mensch>
</Ort>
</Orte>
Dann ergibt ...
<xsl:template match="/">
<ergebnis>
<xsl:value-of select="sum(//Gehalt)"/>
</ergebnis>
</xsl:template>
... dieses Resultat:
<ergebnis>23816.77</ergebnis>
(Anmerkung: derselbe Ansatz unter XPath 3.0 lautet:)
//Gehalt => sum()
Häufig ist aber auch eine bedingte Summierung erforderlich: Es sollen nur jene Zahlen summiert werden, die einer bestimmten Bedingung entsprechen. Beispiel: Sie suchen nach der Summe aller Gehälter bei jenen Menschen, die in Neustadt wohnen.
<ergebnis>
<xsl:value-of
select="sum(/Orte/Ort[@name='Neustadt']/Mensch/Gehalt)"/>
</ergebnis>
Dann lautet das Ergebnis:
<ergebnis>8862.36</ergebnis>
(Anmerkung: derselbe Ansatz unter XPath 3.0 lautet:)
//Ort[@name='Neustadt']//Gehalt => sum()
Noch ein Beispiel: Sie möchten nur jene Gehalter addieren, die kleiner als die Ausgaben sind.
<ergebnis>
<xsl:value-of
select="sum(//Mensch[Gehalt < @Ausgaben]/Gehalt)"/>
</ergebnis>
In diesem Fall lautet das Resultat:
<ergebnis>9047.45</ergebnis>
(Anmerkung: derselbe Ansatz unter XPath 3.0 lautet:)
//Mensch[Gehalt < @Ausgaben]/Gehalt => sum()
bzw.
//Mensch[Gehalt < @Ausgaben]/Gehalt => sum()
Ab XSLT 2.0 haben Sie die Möglichkeit, einen optionalen Null-Wert zu definieren, falls die Sequenz der angesprochenen Items leer ist. Da das Element einkommen im XML-Dokument nicht existiert, ist die Sequenz leer: Zurückgegeben wird der Nullwert. Aus
<ergebnis>
<xsl:value-of
select="sum((//Mensch/einkommen),
'leider nichts gefunden')"/>
</ergebnis>
erhalten Sie also das Resultat:
<ergebnis>leider nichts gefunden</ergebnis>
Interessant ist auch der Ansatz, mit der sum-Funktion eine Zeitkalkulation durchzuführen, die in diesem Fall das Resultat P1Y9M ergibt.
<xsl:value-of select="sum(
(xs:yearMonthDuration('P7M'), xs:yearMonthDuration('P1Y2M'))
)"/>
Ab XSLT 2.0 können Sie der sum-Funktion auch eine selbst definierte Sequenz mitgeben.
<ergebnis>
<xsl:value-of
select="sum(
(7*25.99, 199, 1, (11 to 50)[. mod 9=1])
)"/>
</ergebnis>
Diese selbst definierte Sequenz besteht dann aus den intern berechneten Werten (181.93, 199, 1, 19, 28, 37, 46), das Ergebnis lautet dann:
<ergebnis>511.93</ergebnis>
Interessant finde ich auch diese Alternative, eine Zahlensequenz automatisch zu berechnen (der Ausdruck (1 to 3)!(.*.) gibt die Zahlenreihe 1, 4, 9) und diese dann zu summieren. (Der !-Operator steht aber erst ab XPath 3.0 zur Verfügung.)
<xsl:value-of
select="sum((1 to 3)!(.*.))"/>
Noch eine Alternative (ab XPath 3.0): die Zahlen von 1 bis 10 werden geprüft, ob sie mit Rest durch 2 teilbar sind (ungerade Zahlen). Diese werden einfach berechnet, die anderen Zahlen quadriert.
<xsl:variable
name="v"
select="(1 to 10)!( if (. mod 2=1)
then (.)
else (.*.))"/>
<xsl:for-each select="$v">
<wert>
<xsl:value-of select="."/>
</wert>
</xsl:for-each>
<xsl:value-of select="sum($v)"/>
Damit kommen Sie zu den Teilergebnissen (die Summe als Gesamtwert beträgt 245).
<wert>1</wert>
<wert>4</wert>
<wert>3</wert>
<wert>16</wert>
<wert>5</wert>
<wert>36</wert>
<wert>7</wert>
<wert>64</wert>
<wert>9</wert>
<wert>100</wert
Wichtig ist, dass alle Items der Sequenz als Zahlen gecastet werden können. Das ist bei der folgenden Sequenz (1,2,3,4,5,'Hallo') nicht der Fall:
<ergebnis>
<xsl:value-of select="sum((1 to 5, 'Hallo'))"/>
</ergebnis>
In diesem Fall ist mit einer Fehlermeldung zu rechnen, z.B.
Input to sum() contains a mix of numeric and non-numeric values;
http://www.w3.org/TR/2005/WD-xpath-functions-20050211/#ERRFORG0006.
Für weitergehende Berechnungsmöglichkeiten (z.B. Kumulierungen mit Einzelwertausgabe) siehe auch fold-left, for-each sowie xsl:call-template.
Beachtenswert ist die prozessorabhängige Verarbeitung der Anweisung.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<ergebnis>
<xsl:value-of select="sum((7 * 25.99))"/>
</ergebnis>
</xsl:template>
</xsl:stylesheet>
Je nach verwendetem Prozessor erhalten Sie unter xsl:stylesheet version="1.0" eine Fehlermeldung: "The value is not a node-set." oder ein Ergebnis, das auf automatischen Double-Type-Cast des Wertes schließen lässt.
<ergebnis>181.92999999999998</ergebnis>
Ändern Sie die XSL-Stylesheet-Version auf 2.0 bzw. 3.0, so generieren einige Prozessoren ein Ergebnis, das einen automatischen Decimal-Type-Cast des Wertes vermuten lässt.
<ergebnis>181.93</ergebnis>
Wenn Sie die Typkontrolle nicht dem System überlassen möchten, sind Sie gut beraten, den Type-Cast auf xs:decimal zu stellen.
<xsl:template match="/">
<ergebnis>
<xsl:value-of
select="sum(
for $m in //Kauf return
($m/xs:decimal(preis) * $m/xs:decimal(anzahl))
)"/>
</ergebnis>
</xsl:template>
Aufschlussreich ist hier eine Sequenz von Testwerten. Pro test-Element sollen dessen Attribute @p1 und @p2 miteinander multipliziert werden; der jeweils erwartete Wert ist mit dem entsprechenden Attribut vorgegeben. Zusätzlich finden Sie in @real jene Werte, die sich bei automatischem Double-Type-Casting tatsächlich ergeben.
<xsl:variable name="vtestsequenz">
<test p1="12.99" p2="11"
erwartet="142.89"
real="142.89000000000001"/>
<test p1="12.99" p2="22"
erwartet="285.78"
real="285.78000000000003"/>
<test p1="12.99" p2="44"
erwartet="571.56"
real="571.5600000000001"/>
<test p1="151.23" p2="6"
erwartet="907.38"
real="907.3799999999999"/>
<test p1="151.23" p2="3"
erwartet="453.69"
real="453.68999999999994"/>
<test p1="25.99" p2="7"
erwartet="181.93"
real="181.92999999999998"/>
<test p1="2.1" p2="3"
erwartet="6.3"
real="6.300000000000001"/>
<test p1="2.15" p2="3"
erwartet="6.45"
real="6.449999999999999"/>
<test p1="2.18" p2="3"
erwartet="6.54"
real="6.540000000000001"/>
<test p1="2.2" p2="3"
erwartet="6.6"
real="6.6000000000000005"/>
<test p1="2.23" p2="3"
erwartet="6.69"
real="6.6899999999999995"/>
</xsl:variable>
Das folgende XSL-Stylesheet verfolgt das Ziel, das prozessorabhängige, im Einzelfall möglicherweise unerwartete, Ergebnis sichtbar zu machen.
<xsl:template match="/">
<ergebnis>
<xsl:for-each select="$vtestsequenz/test">
<xsl:if test="@p1 * @p2 != @erwartet">
<Fehler>
<xsl:value-of select="@p1 * @p2"/>
</Fehler>
</xsl:if>
</xsl:for-each>
<summeprod>
<xsl:value-of
select="sum($vtestsequenz/test/(@p1 * @p2))"/>
</summeprod>
<summeerwartet>
<xsl:value-of
select="sum($vtestsequenz/test/@erwartet)"/>
</summeerwartet>
<summeerwartet_decimal>
<xsl:value-of
select="sum($vtestsequenz/test/xs:decimal(@erwartet))"/>
</summeerwartet_decimal>
<summereal>
<xsl:value-of
select="sum($vtestsequenz/test/@real)"/>
</summereal>
</ergebnis>
</xsl:template>
<ergebnis>
<Fehler>142.89000000000001</Fehler>
<Fehler>285.78000000000003</Fehler>
<Fehler>571.5600000000001</Fehler>
<Fehler>907.3799999999999</Fehler>
<Fehler>453.68999999999994</Fehler>
<Fehler>181.92999999999998</Fehler>
<Fehler>6.300000000000001</Fehler>
<Fehler>6.449999999999999</Fehler>
<Fehler>6.540000000000001</Fehler>
<Fehler>6.6000000000000005</Fehler>
<Fehler>6.6899999999999995</Fehler>
<summeprod>2575.81</summeprod>
<summeerwartet>2575.8099999999995</summeerwartet>
<summeerwartet_decimal>2575.81</summeerwartet_decimal>
<summereal>2575.81</summereal>
</ergebnis>
Über => können Sie so vorgehen:
(1 to 10) => sum()
Defaut-Ausgabe, wenn die Sequenz leer ist:
() => sum('leider nix')
Wichtig ist auch hier, dass alle Items der Sequenz als Zahl gecastet werden können.
(
xs:integer(9),
xs:decimal("8"),
round(7.99999999999, 2) => xs:double(),
xs:decimal(true()),
xs:float(false()),
xs:integer(true())
)
=> sum()
true() wird als "1", false() als "0" interpretiert. Das Ergebnis ist in diesem Fall:
27
Der Type-Cast schlägt jedoch fehl bei:
(
xs:negativeInteger(5),
xs:short(45678),
current-date(),
xs:double(current-dateTime()),
xs:decimal("Moin")
)
=> sum()
wg / 24. März 2020
Fragen? Anmerkungen? Tipps?
Bitte nehmen Sie Kontakt zu mir auf.
ᐅ math:tan
V.i.S.d.P.: Wilfried Grupe * Klus 6 * 37643 Negenborn
☎ 0151. 750 360 61 * eMail: info10@wilfried-grupe.de