Reputation: 1340
I have the following XML:
<STATUSLIST>
<STATUS>
<TYPE VALUE="1"/>
<DATE>19910000</DATE>
</STATUS>
<STATUS>
<TYPE VALUE="1"/>
<DATE>19470000</DATE>
</STATUS>
<STATUS>
<TYPE VALUE="2"/>
<DATE>19470000</DATE>
</STATUS>
</STATUSLIST>
And I would like to match the STATUS
where TYPE/@VALUE = '2'
and not(//STATUSLIST/STATUS/DATE > DATE)
.
In this case it would be the 3rd STATUS
.
When I apply the type with the latest date I get nothing because it can't match both. What I would like is to match the TYPE/@VALUE = '2'
first and in that match get the one with the latest date.
Any clue?
Cheers,
Tuno
Upvotes: 1
Views: 2100
Reputation: 122394
While your own solution of STATUS[TYPE/@VALUE = '2'][not(//STATUSLIST/STATUS[TYPE/@VALUE = '2']/DATE > DATE)]
will work, it is not particularly efficient for large numbers of entries as you're comparing every STATUS
with every other STATUS
, so O(N2). More efficient would be to implement it as a tail-recursive function (as you say you're using XSLT 2.0) such as the following:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:local="urn:local" exclude-result-prefixes="local">
<xsl:function name="local:latestByDate" as="item()*">
<xsl:param name="seq" as="item()*"/>
<xsl:sequence select="local:latestByDate($seq, ())" />
</xsl:function>
<xsl:function name="local:latestByDate" as="item()*">
<xsl:param name="seq" as="item()*" />
<xsl:param name="maxSoFar" as="item()*" />
<xsl:choose>
<xsl:when test="$seq">
<!-- calculate the new maxSoFar, comparing the current max with
the first item in $seq. Note the use of not(x<=y) instead of
x>y, so the test is true if $maxSoFar is the empty sequence -->
<xsl:variable name="newMax"
select="if(not($seq[1]/DATE <= $maxSoFar/DATE))
then $seq[1] else $maxSoFar"/>
<xsl:sequence select="local:latestByDate(
$seq[position() gt 1], $newMax)" />
</xsl:when>
<xsl:otherwise>
<!-- we have reached the end of $seq, return the max -->
<xsl:sequence select="$maxSoFar" />
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<!-- example of how to call the function -->
<xsl:template match="/*">
<xsl:copy-of select="local:latestByDate(STATUS[TYPE/@VALUE='2'])" />
</xsl:template>
</xsl:stylesheet>
This function makes a single pass over the list of nodes (so O(N)), keeping track at each step of which element in the input sequence has the latest DATE
.
Upvotes: 0
Reputation: 1340
My solution: STATUS[TYPE/@VALUE = '2'][not(//STATUSLIST/STATUS[TYPE/@VALUE = '2']/DATE > DATE)]
.
Upvotes: 1
Reputation: 109100
If I understand what you want correctly, you cannot do this all in XPath v1 due to the lack of a maximum function. So get all the STATUS
elements with the desired TYPE:
/STATUSLIST/STATUS[TYPE/@VALUE=2]
and then sort or maximum function in the calling environment.
Upvotes: 0