Reputation: 51
I would like to find a "nicer" solution to get the minimum and maximum values of attributes and save them into accesible variables. I would love to get away from the for-each-loop too. How is that possible?
My XML:
<Rows>
<Entry value1="16,423" value2="18,123" />
<Entry value1="423" value2="11,588" />
<Entry value1="1,168" value2="521" />
</Rows>
And my XSL:
<xsl:for-each select="Rows/Entry/@value1|Rows/Entry/@value2">
<xsl:sort select="." data-type="number" />
<xsl:choose>
<xsl:when test="position() = 1">
<xsl:variable name="min" select="format-number(translate(.,',',''),'#')" />
</xsl:when>
<xsl:when test="position() = last()">
<xsl:variable name="max" select="format-number(translate(.,',',''),'#')" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
The desired output should be $min=423 and $max=18123 as numbers and accesible outside the for-each-loop
Upvotes: 0
Views: 764
Reputation: 167716
Well there is XSLT 2.0 since 2007 (implemented by XSLT processors like Saxon 9, AltovaXML, XmlPrime) where you can simply do (assuming you have the declaration xmlns:xs="http://www.w3.org/2001/XMLSchema"
on your xsl:stylesheet
element):
<xsl:variable name="min" select="min(Rows/Entry/(@value1, @value2)/xs:decimal(translate(., ',', ''))"/>
<xsl:variable name="max" select="max(Rows/Entry/(@value1, @value2)/xs:decimal(translate(., ',', ''))"/>
If you really want to store a formatted string in a variable you can of course do that as well with e.g.
<xsl:variable name="min" select="format-number(min(Rows/Entry/(@value1, @value2)/xs:decimal(translate(., ',', '')), '#')"/>
<xsl:variable name="max" select="format-number(max(Rows/Entry/(@value1, @value2)/xs:decimal(translate(., ',', '')), '#')"/>
As for XSLT 1.0, there I think the sorting with for-each
is the right approach but you would need to pull the xsl:variable
outside the for-each
e.g.
<xsl:variable name="min">
<xsl:for-each select="Rows/Entry/@value1|Rows/Entry/@value2">
<xsl:sort select="translate(., ',', '')" data-type="number"/>
<xsl:if test="position() = 1">
<xsl:value-of select="format-number(., '#')"/>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="max">
<xsl:for-each select="Rows/Entry/@value1|Rows/Entry/@value2">
<xsl:sort select="translate(., ',', '')" data-type="number"/>
<xsl:if test="position() = last()">
<xsl:value-of select="format-number(.,'#')" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
As an alternative you could replace the for-each
with apply-templates
and then write a template matching @value1 | @value2
but while I think most tasks to transform nodes are better done using push style in XSLT I think for finding a minimum or maximum value the for-each
is fine.
Upvotes: 2
Reputation: 3428
I'm not sure if it is absolutely correct but I tried this for min
(/Rows/Entry/@value1|/Rows/Entry/@value2)[not((/Rows/Entry/@value1|/Rows/Entry/@value2) < .)]
and this for max
(/Rows/Entry/@value1|/Rows/Entry/@value2)[not((/Rows/Entry/@value1|/Rows/Entry/@value2) > .)]
and it gave me values you mentioned. But for simplification I worked with xml with values without ",".
Upvotes: 1