Reputation: 33
I have a string (in a variable) that has a list of numbers separated by space or comma. I need to sum the numbers in the string. example string "1,2,5,12,3" or "1 2 5 12 3"
Is there a way to add the numbers within the string and return the total?
Upvotes: 3
Views: 5597
Reputation: 243529
This much shorter transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="text()" name="sumStringList">
<xsl:param name="pText" select="."/>
<xsl:param name="pSum" select="0"/>
<xsl:param name="pDelim" select="','"/>
<xsl:choose>
<xsl:when test="not(string-length($pText) >0)">
<xsl:value-of select="$pSum"/>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vnewList"
select="concat($pText,$pDelim)"/>
<xsl:variable name="vHead" select=
"substring-before($vnewList, $pDelim)"/>
<xsl:call-template name="sumStringList">
<xsl:with-param name="pText" select=
"substring-after($pText, $pDelim)"/>
<xsl:with-param name="pSum" select="$pSum+$vHead"/>
<xsl:with-param name="pDelim" select="$pDelim"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
when applied on the following XML document:
<t>1,2,5,12,3</t>
produces the wanted, correct result:
23
Explanation: Recursively called named template that also matches a text node. A sentinel (appended comma) is added to speed up and streamline processing.
II. XSLT 2.0 solution:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pDelim" select="','"/>
<xsl:template match="text()">
<xsl:sequence select=
"sum(for $s in tokenize(.,$pDelim)
return number($s)
)
"/>
</xsl:template>
</xsl:stylesheet>
When applied on the same XML document (above), this transformation produces the same wanted, correct answer:
23
Here we use the standard XPath 2.0 function tokenize()
and we must convert every resulting token to number (using the number()
function) before finally applying the standard XPath function sum()
.
Upvotes: 3
Reputation: 66783
Here is an XSLT 1.0 solution
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:variable name="listOfValues" select="'1,2,5,12,3'" />
<xsl:call-template name="splitAndAdd">
<xsl:with-param name="list" select="$listOfValues"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="splitAndAdd">
<xsl:param name="list" />
<xsl:param name="delimiter" select="','"/>
<xsl:param name="total" select="0" />
<xsl:variable name="newList">
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<xsl:value-of select="normalize-space($list)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="concat(normalize-space($list),$delimiter)" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="token"
select="substring-before($newList, $delimiter)" />
<xsl:variable name="remaining"
select="normalize-space(substring-after($newList, $delimiter))" />
<xsl:variable name="newTotal" select="$total + number($token)" />
<xsl:choose>
<xsl:when test="$remaining">
<xsl:call-template name="splitAndAdd">
<xsl:with-param name="delimiter" select="$delimiter"/>
<xsl:with-param name="list" select="$remaining"/>
<xsl:with-param name="total" select="$newTotal" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$newTotal" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0